我有一个具有以下定义的类:
public abstract class A<T> implements Iterator<B> {}
以下对next()的调用将返回一个Object而不是B:
A a = new SomethingThatExtendsA();
B b = a.next();
我已经搜索了很长时间,并且无法弄清楚为什么这个next()调用无法编译。有人能够为我描述这种行为吗?
编辑原文是模板化的,因为这似乎很重要。
编辑以获得进一步说明:这是编译时问题,而不是运行时问题。 SomethingThatExtendsA()的实现;在这种情况下,在编译时应该是无关紧要的。
答案 0 :(得分:5)
所以我们有这个代码:
public abstract class A<T> implements Iterator<B> {}
[...]
A a = new SomethingThatExtendsA();
a.next();
A
是泛型类型,但您已使用原始类型定义a
。忽略=
的右侧,我们只对静态类型感兴趣:
A/*<Something>*/ a = ...;
编译器应该在这里给你警告。 (至少相对较新版本的javac会在Oracle javac中发出rawtypes
警告。)注意编译器的警告。 (当javac没有发出警告时,这不是很好吗?)
所以现在我们处于a
类型既是原始类型又是Iterator<B>
的情况。这是一个非常令人困惑的情况,只是令人难以置信的困难影响。我们甚至不应该这样做 - 我们应该避免混合泛型和原始类型。因此,Java语言规范采用了简单的方法并丢弃了部分泛型类型信息。
因此,不要混合原始类型和泛型类型。只需使用所有泛型,你应该没事。
答案 1 :(得分:0)
public abstract class A implements Iterator<B> {}
class B {}
class SomethingThatExtendsA extends A {
// implement A methods
}
A a = new SomethingThatExtendsA();
a.next();
此代码非常正确并返回B对象。可能你错过了SomethingThatExtendsA
班的内容。
通过致电:
进行测试B b1 = new Object(); // compilation error
B b2 = a.next(); // all is OK
<强>更新强>
在SomethingThatExtendsA中更改签名
public Object next() { }
至public B next() {}
答案 2 :(得分:0)
SomethingThatExtendsA
课程在这里是一个红鲱鱼。
List<String> list = new ArrayList<String>();
list.add("foo");
Iterator iterator = list.iterator(); // you should get a warning on this line...
String foo = iterator.next(); // ... and a compile error on this line
问题是原始类型是一种可怕的丑陋黑客攻击,只会导致它们周围的所有泛型停止工作。具体而言,Iterator
与Iterator<String>
,Iterator<Object>
甚至Iterator<?>
的类型不同。相反,Iterator
表示“如果我使用预先泛型版本的Java编译,我将获得的Iterator
类”。由于Iterator
的pre-generics版本始终返回Object
,所以这就是你得到的。而且,如果没有强制转换,您就无法将类型Object
的值分配给更具体的类型(如String
或类型B
)。
Java语言规范说:
原始类型的使用仅允许作为遗留代码兼容性的让步。在将通用性引入Java编程语言之后编写的代码中使用原始类型强烈建议不要。 未来版本的Java编程语言可能会禁止使用原始类型。