在原始类类型上忽略显式方法类型参数; 编译bug?
我在调用带有显式类型参数的generics方法时遇到编译器错误,就好像未考虑显式类型参数一样。 最小的例子:
class CastExample { static class ThingProducer { public T getThing() { return null; } } static class ThingA {} public static void main(String... args) { ThingProducer thingProducer = new ThingProducer(); ThingA thingA = thingProducer.getThing(); // compile error here } }
ThingProducer
是一个原始类型,因为类有一个类型参数,但在调用getThing
我们没有引用类类型参数,而是提供方法类型参数。 根据我对JLS的理解,这应该是合法的,但它给了我这个错误:
incompatible types: Object cannot be converted to ThingA
如果我,错误消失
- 从
ThingProducer
删除 - 或使
getThing
静态 - 声明
thingProducer ThingProducer
而不是原始类型ThingProducer
这是编译器错误吗? 如果没有,JLS中的哪个规则定义了这种行为?
Java语言规范的4.8节回答了你的问题:
未从其超类或超接口inheritance的原始类型C的构造函数(第8.8节),实例方法(第8.4节,第9.4节)或非静态字段(第8.3节)的类型是对应于的原始类型在与C对应的generics声明中擦除其类型
在您的示例中, getThing()
是一个“原始类型C的实例方法… [在这种情况下, ThingProducer
],它不是inheritance的”。 根据JLS,它的类型是“与通用声明中类型的擦除相对应的原始类型”。 在getThing()
的generics声明中,它的类型T
是无界的,这意味着它的擦除是java.lang.Object
。
请注意,规范并没有说getThing()
的类型是通过擦除它所属的原始类型(即ThingProducer
)构造的类型 – 它实际上是getThing()
本身的擦除,这意味着两个类型参数( T
和S
)都被擦除。
[旁白:在我的原始答案中,我引用了规范的另一句话:“将类型参数传递给原始类型的非静态类型成员是一个编译时错误,该成员不是从其超类或超接口inheritance的。” 我对该句子的原始读取是,编译器需要为上面的语法发出编译时错误,因为我断定你试图“将类型参数传递给原始类型的非静态类型成员”。 但我改变了主意 :我认为最后一句是指非静态类型成员(即嵌套类型),而不仅仅是非静态通用成员。
当然,如果没有从规范中引用这一点,就不会完成对4.8节的讨论:
原始类型的使用仅允许作为遗留代码兼容性的让步。 在将generics引入Java编程语言之后编写的代码中使用原始类型是非常不鼓励的。 未来版本的Java编程语言可能会禁止使用原始类型。
作为已接受答案的补充,如果您只是想尽可能简单地修复编译错误,并且您没有使用类的类型参数
,那么最合适的修复(感谢@Tunaki)是
ThingProducer> thingProducer = new ThingProducer();
代替
ThingProducer thingProducer = new ThingProducer();
这使我们处于generics世界,同时记录了类型参数的重要性。
(在这个缩减的例子中,改变ThingProducer会更有意义,但我怀疑在现实世界中,任何得到这个错误的人都可能正在处理他们无法改变的遗留类 – 至少我是这样的。)