编程语言
首页 > 编程语言> > 协变结构在Java中捕获错误失败

协变结构在Java中捕获错误失败

作者:互联网

请考虑以下Java类定义:

class Animal {}
class Lion extends Animal {}

在为动物定义协变Cage时,我在Java中使用此代码:

class Cage<T extends Animal> {
void add(T animal) { System.out.println("Adding animal..."); }
}

但是以下Java示例……

public static void main(String... args) {
    Cage<? extends Animal> animals = null;
    Cage<Lion> lions = null;
    animals = lions;         // Works!
    animals.add(new Lion()); // Error!
}

…无法编译时出现以下错误:

The method add(capture#2-of ? extends Animal)
in the type Cage
is not applicable to for the arguments (Lion)

这样做是因为否则可以在动物=狮子之后添加像Tiger这样的不同类型并在运行时失败?

如果只有一个动物的子类型,是否可以制定一个不会拒绝它的特殊(假设)规则?

(我知道我可以用动物替换添加的T.)

解决方法:

在java中:

Cage<? extends Animal> animals = null;

这是一个笼子,但你不知道它接受什么样的动物.

animals = lions;         // Works!

好吧,你没有添加任何关于笼养动物的意见,所以狮子没有任何期望.

animals.add(new Lion()); // Error!

你不知道什么样的笼养动物.在这个特殊的情况下,它恰好是你把狮子放进去的狮子笼,很好,但是允许这样做的规则只允许将任何种类的动物放入任何笼子里.它被正确禁止.

在斯卡拉:
Cage [T]:如果B延伸A,则Cage [B]应被视为Cage [A].

鉴于此,允许动物=狮子.

但这与java不同,类型参数绝对是Animal,而不是通配符?扩展动物.你可以将动物放入笼中[动物],狮子是动物,所以你可以把狮子放在笼子[动物]中,它可能是一只笼子[Bird].这非常糟糕.

除了它实际上是不被允许的(幸运的是).您的代码不应该编译(如果它为您编译,您发现编译器错误).协变通用参数不允许作为方法的参数出现.原因恰恰在于允许将狮子放入鸟笼中.它在Cage的定义中显示为T,它不能作为方法添加的参数出现.

所以这两种语言都不允许将狮子放入鸟笼.

关于您的更新问题.

这样做是因为否则可以添加一只老虎?

是的,这当然是原因,类型系统的要点是使这不可能.这会导致运行时错误吗?很可能,它会在某个时刻,但不会在您调用add时,因为在运行时不会检查实际类型的泛型(类型擦除).但是类型系统通常拒绝每个程序,它不能证明(某种类型)错误不会发生,而不仅仅是它可以证明它们确实发生的程序.

如果只有一个动物的子类型,是否可以制定一个不会拒绝它的特殊(假设)规则?

也许.请注意,您仍有两种动物,即动物和狮子.所以重要的事实是Lion实例属于这两种类型.另一方面,Animal实例不属于Lion类型. animals.add(新的狮子())可以被允许(笼子可以是任何动物的笼子,或只是狮子,两者都可以),但是animals.add(新动物())不应该(因为动物可能是仅狮子笼).

但无论如何,这听起来是一个非常糟糕的主意.面向对象系统中的继承点是,稍后,在其他地方工作的其他人可以添加子类型,这不会导致正确的系统变得不正确.实际上,旧代码甚至不需要重新编译(也许你没有源代码).有了这样的规则,那就不再是真的了

标签:java,inheritance,covariance,generics,bounded-wildcard
来源: https://codeday.me/bug/20190620/1248582.html