Java 如何使具有有界类型参数的方法排除一个子类?

Java 如何使具有有界类型参数的方法排除一个子类?,java,inheritance,bounded-types,Java,Inheritance,Bounded Types,让我们假设长臂猿、猩猩、大猩猩、黑猩猩和人类都是类人猿。我对这种关系进行了相应的建模 class Ape {} class Gibbon extends Ape {} class Orangutan extends Ape {} class Gorilla extends Ape {} class Chimpanzee extends Ape {} class Human extends Ape {} 现在,我想编写一个带有有界类型参数的hunt()方法,用于捕捉猿类 public stati

让我们假设长臂猿、猩猩、大猩猩、黑猩猩和人类都是类人猿。我对这种关系进行了相应的建模

class Ape {}
class Gibbon extends Ape {}
class Orangutan extends Ape {}
class Gorilla extends Ape {}
class Chimpanzee extends Ape {}
class Human extends Ape {}
现在,我想编写一个带有有界类型参数的hunt()方法,用于捕捉猿类

public static <T extends Ape> void hunt(T type) {}
publicstaticvoidhunt(T类型){

让我们假设猎杀非人类猿类没有什么错,因为它们只是动物。然而,猎杀人类是错误的,因为这是谋杀。如何重新编写上述有界类型参数以将人类排除在法律参数之外?我对这里的例外情况不感兴趣。如果使用人工参数调用,我根本不希望hunt()方法编译。

我不相信有办法做到这一点。您可以创建一个子类型的Ape来排除人类,或者您可以在方法本身中将人类作为一个类型进行检查(这将违反开闭原则):

公共静态无效搜索(T型){
if(输入instanceOf Human){
抛出新的非法辩论例外(“猎杀人类是不道德的,你这个怪物”);
}
. . .
}

我认为没有办法做到这一点。您可以创建一个子类型的Ape来排除人类,或者您可以在方法本身中将人类作为一个类型进行检查(这将违反开闭原则):

公共静态无效搜索(T型){
if(输入instanceOf Human){
抛出新的非法辩论例外(“猎杀人类是不道德的,你这个怪物”);
}
. . .
}

您不能这样做。没有办法指定一个绑定来表示“除此之外的任何子类”

即使你可以,它也不会阻止你调用它:

chimp.hunt((Ape) human);
我会绕过去的

您所能做的就是在运行时检查这一点


您可以通过一些工具,例如(由Google编写,我是一名员工,我贡献;其他工具可用)编写编译时检查,以确保参数既不是
Ape
也不是
Human
;但这超出了“纯”Java的能力。

您不能这样做。没有办法指定一个绑定来表示“除此之外的任何子类”

即使你可以,它也不会阻止你调用它:

chimp.hunt((Ape) human);
我会绕过去的

您所能做的就是在运行时检查这一点


您可以通过一些工具,例如(由Google编写,我是一名员工,我贡献;其他工具可用)编写编译时检查,以确保参数既不是
Ape
也不是
Human
;但是,这超出了“纯”Java的能力。

您不能按照预期的方式排除特定的子类。但是,您可以创建一个接口“isHuntable”,由您想要捕猎的所需动物实现,并将其用作方法的类型,而不是泛型类型绑定。另一个可能不那么优雅的解决方案是在层次结构中创建另一个称为“猴子”的级别,例如,它从猿扩展而来,让所有动物都从猿扩展而来,然后将猴子类型用于方法。不过我还是同意第一种方法。您可以使用显式类型检查,但这会破坏代码中的“打开-关闭”原则,因此最好将这些检查用于类型系统


只是对接口行为契约的概念进行了一点扩展,这是一个功能强大的工具,但遗憾的是它没有得到充分利用。你们的猿类之间有一种“是-是”的关系,这种关系将你们的主体紧密地结合在一起。另一方面,“可搜索”不是该层次结构的固有或定义特征,更像是您希望添加到该层次结构子集的附加条件/行为/检查。通过将契约(接口实现)添加到您希望具有该行为的子集,可以更好地实现这一点。这将允许在未来添加更多的契约(接口实现),以最少的重构、最大的语义清晰性、更少的bug,并且不会将类型与行为紧密绑定。简而言之,你不会违反利斯科夫替换原理,也不会违反开闭原理,也不会违反任何坚实的原理

您不能按照预期的方式排除特定的子类。但是,您可以创建一个接口“isHuntable”,由您想要捕猎的所需动物实现,并将其用作方法的类型,而不是泛型类型绑定。另一个可能不那么优雅的解决方案是在层次结构中创建另一个称为“猴子”的级别,例如,它从猿扩展而来,让所有动物都从猿扩展而来,然后将猴子类型用于方法。不过我还是同意第一种方法。您可以使用显式类型检查,但这会破坏代码中的“打开-关闭”原则,因此最好将这些检查用于类型系统


只是对接口行为契约的概念进行了一点扩展,这是一个功能强大的工具,但遗憾的是它没有得到充分利用。你们的猿类之间有一种“是-是”的关系,这种关系将你们的主体紧密地结合在一起。另一方面,“可搜索”不是该层次结构的固有或定义特征,更像是您希望添加到该层次结构子集的附加条件/行为/检查。通过将契约(接口实现)添加到您希望具有该行为的子集,可以更好地实现这一点。这将允许在未来添加更多的契约(接口实现),以最少的重构、最大的语义清晰性、更少的bug,并且不会将类型与行为紧密绑定。简而言之,你不会违反利斯科夫替换原理,也不会违反开闭原理,也不会违反任何坚实的原理

我会让你的
hunt
方法非静态;毕竟,这是对物体本身的一种作用。然后,您可以定义所有猿的行为:

public void hunt() {
    this.die(); // oh no!
}
当然,人类凌驾于这一基本理念之上:

@Override
public void hunt() {
    throw new IllegalMurderException("Human horn hunting is against intergalactic law");
}
#isHuntable
添加一个复选框是可取的,因为它与隐喻保持一致
public interface Living {
    public void die();
}

public interface Huntable extends Living {
    default public void hunt() {
        this.die();
    }
    //could use this for a generic exception in the interface too
    public boolean isHuntable(); 
}

public interface Ape extends Huntable {} // already Huntable

public interface Human extends Ape {
    //And finally, we break that behavioral contract like above
}
public static void hunt(Ape example) { ... }

public static void hunt(Human example) throws HuntingException {
    throw new HuntingException("cannot hunt humans");
}
Main.hunt((Ape) someHuman);