协变结构在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