2015-09-27 94 views
3

所以,我正在浏览Oracle Java教程,特别是这段代码;Java泛型和instanceof关键字

List<EvenNumber> le = new ArrayList<>(); 
List<? extends NaturalNumber> ln = le; 
ln.add(new NaturalNumber(35)); // compile-time error 

哪能找到here。这是我的理解(并请纠正我,如果我错了),上述代码将无法正常工作,因为编译器不知道ln引用一个EvenNumbers列表,所以这可以防止你意外添加任何可能是一个对象用于列表的元素的超类型。如果是这种情况,那么为什么如果你有像Number num = new Integer(6);这样的语句,编译器能够正确地确定num是一个Integer对象,如果你这样写if语句的话; if (num instanceof Integer) {...}

我想我的问题是,如何能够确定num指的是一个Integer对象在第二个例子中,但无法确定ln编译器指的是List<EvenNumber>对象在第一个例子吗?

+2

“为什么如果你有一个像Number num = new Integer(6)的语句;编译器能够正确地确定num是一个Integer对象” - 编译器不知道! 'instanceof'在运行时计算。 – user2357112

+0

另请参阅我的[文章](http://bayou.io/draft/Capturing_Wildcards.html)通配符以防万一。 – ZhongYu

回答

1

通常,编译器能够确定很多事情,并且在优化代码时使用它的观察值。

但是,Java是statically and strongly类型化语言,编译器在类型安全性方面没有自由度。

1)禁止添加到ln列表中,因为它不知道它应该包含哪种类型的元素。在你的例子中,这个规则阻止了列表进入无效状态。

2)“...的编译器能够正确地确定num是一个整数...”

事实并非如此,编译器不能确定它(虽然它可以运行Java是弱类型语言)。

3)num instanceof Integer

这是在运行时评估,而不是在编译时。然而,下面将产生编译时错误:

num instanceof String

因为Number永远不能成为String

+0

哎呀,对不起。我的意思是“ln”而不是“le”。我编辑了我的帖子。我没有意识到instanceof是在运行时进行评估的。它如何能够弄清楚num是一个Integer对象而不是一个Number对象? – BlaqICE

+0

@BlaqICE每个Java对象都包含有关其类型的信息:[Object header](http://stackoverflow.com/questions/26357186/what-is-in-java-object-header)。 –

1

你的两个例子是彼此相反的。

Integer一个Number,所以

List<Number> list; 
list.add(new Integer(1)); // compiles 

EvenNumber一个NaturalNumber而不是其他方式,所以

List<EvenNumber> list; 
list.add(new NaturalNumber(1)); // compile error 

,因为虽然EvenNumberNaturalNumberNaturalNumber不是(必然)是EvenNumber

如果您在参考的情况下交换IntegerNumber然后返工它,它应该是有意义的,即这将编译:

List<NaturalNumber> list; 
list.add(new EvenNumber(2)); // compiles OK 
+0

我想你正在描述一个'List ln = le;'会发生的情况,但事实并非如此。这会在上面的行中导致编译错误,但是在执行'add'时不会导致编译错误。 – Makoto

+0

@Makoto号我想说的是他将一个Integer添加到Number列表中的类比是无关紧要的,因为他的代码中有相反的类层次结构 - 也就是说Integer是Number的子类,EvenNumber是NaturalNumber,但他的代码试图将NaturalNumber添加到EvenNumber列表中,这就像试图将Number添加到Integer列表中......这显然不起作用。 – Bohemian

0

的主要原因,这是不能够编译是由于路? extends T的行为。一般来说,人们应该熟悉Producer Extends, Consumer Super或简称PECS。

现在,我没有看过这个特定的代码示例,但我想象的层次是EvenNumber延伸NaturalNumber。这使得赋值语句有效,因为EvenNumberNaturalNumber(尽管我敢打赌,数学家们对此很嗤之以鼻)。

在上面突出显示的场景中,您无法向收藏添加任何内容的原因是因为它主要是为了阅读。也就是说,该清单是生产商,因此它与extends绑定。

如果要同时读取写入集合,则只需要忽略通配符。

你的第二个例子有什么都没有与泛型有关,而是简单的继承。由于Integer继承自Number,所以我们可以说Integer是-a Number,因此可以被视为一个数字。 任何继承的类都有此能力。查看Liskov Substitution Principle了解更多信息。