所以,我正在查看Oracle Java教程,特别是这段代码;
List<EvenNumber> le = new ArrayList<>();
List<? extends NaturalNumber> ln = le;
ln.add(new NaturalNumber(35)); // compile-time error
可以找到here。这是我的理解(如果我错了,请纠正我)上面的代码不起作用,因为编译器不知道ln
引用了EvenNumbers列表,所以这可以防止你意外添加任何可能是List的元素的超类型的对象。如果是这种情况,那么为什么如果你有一个类似Number num = new Integer(6);
的语句,如果你编写if语句,编译器就能正确地确定num是一个Integer对象;
if (num instanceof Integer) {...}
?
我想我的问题是,编译器如何确定num
在第二个示例中引用Integer
对象,但无法确定ln
是否引用到第一个例子中的List<EvenNumber>
对象?
答案 0 :(得分:1)
你的两个例子是相反的。
Integer
是Number
,所以
List<Number> list;
list.add(new Integer(1)); // compiles
但EvenNumber
是NaturalNumber
,不是相反,所以
List<EvenNumber> list;
list.add(new NaturalNumber(1)); // compile error
因为EvenNumber
是NaturalNumber
,NaturalNumber
不是(必然)EvenNumber
。
如果你在参考案例中交换Integer
和Number
然后重做它,它应该有意义,即这将编译:
List<NaturalNumber> list;
list.add(new EvenNumber(2)); // compiles OK
答案 1 :(得分:1)
通常,编译器能够确定很多东西,并在优化代码时使用它的观察结果。
但是,Java是statically and strongly类型语言,编译器在类型安全方面没有自由。
1)禁止添加ln
列表,因为人们并不确切知道它应该包含哪种类型的元素。在您的示例中,此规则阻止将列表置于无效状态。
2)&#34; ...编译器能够正确地确定num是一个整数...&#34;
不正确,编译器不能确定它(尽管如果Java是弱类型语言的话,它可能会发生。)
3)num instanceof Integer
这是在运行时评估的,而不是在编译时评估的。但是,以下将产生编译时错误:
num instanceof String
因为Number
永远不会是String
。
答案 2 :(得分:0)
无法编译的主要原因是? extends T
的行为方式。一般来说,人们应该熟悉Producer Extends, Consumer Super或简称PECS。
现在,我还没有看过这个特定的代码示例,但我认为层次结构EvenNumber
扩展了NaturalNumber
。这使得赋值语句有效,因为EvenNumber
是NaturalNumber
(尽管我打赌数学家正在嘲笑这个)。
在上面突出显示的场景中,您无法向集合添加任何内容的原因是它主要用于阅读。也就是说,列表是生成器,因此它与extends
绑定。
如果你想同时阅读和写一个集合,你只需要留下通配符。
你的第二个例子根本没有 nothing 与泛型有关,而是简单的继承。由于Integer
继承自Number
,我们可以说Integer
是-a Number
,因此可以视为数字。 继承的任何类都具有此功能。有关详细信息,请查看Liskov Substitution Principle。