Java 泛型和 instanceof 关键字

Java generics and the instanceof keyword

所以,我正在查看 Oracle Java 教程,特别是这段代码;

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); 这样的语句,如果你写一个这样的 if 语句,编译器就能够正确地确定 num 是一个 Integer 对象; if (num instanceof Integer) {...}?

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

你的两个例子是相反的。

一个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

无法编译的主要原因是 ? extends T 的行为方式。一般来说,应该熟悉Producer Extends, Consumer Super,简称PECS。

现在,我还没有查看这个特定的代码示例,但我认为层次结构是 EvenNumber 扩展 NaturalNumber。这使得赋值语句有效,因为 EvenNumberNaturalNumber(尽管我敢打赌数学家们对此嗤之以鼻)。

在上面突出显示的场景中,您无法向集合中添加任何内容的原因是它主要用于读取。即list是一个producer,所以绑定了extends.

如果您想同时读取 写入一个集合,您可以省略通配符。

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

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

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

1) 禁止添加到ln 列表中,因为人们不知道它应该包含哪种类型的元素。在您的示例中,此规则阻止将列表置于无效状态。

2) "...编译器能够正确确定 num 是一个整数..."

不正确,编译器不能确定它(尽管如果 Java 是弱类型语言它可以)。

3) num instanceof Integer

这是在运行时评估的,而不是在编译时评估的。但是,以下会产生编译时错误:

num instanceof String

因为 Number 永远不可能是 String