在原始类类型上忽略显式方法类型参数; 编译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()本身的擦除,这意味着两个类型参数( TS )都被擦除。

[旁白:在我的原始答案中,我引用了规范的另一句话:“将类型参数传递给原始类型的非静态类型成员是一个编译时错误,该成员不是从其超类或超接口inheritance的。” 我对该句子的原始读取是,编译器需要为上面的语法发出编译时错误,因为我断定你试图“将类型参数传递给原始类型的非静态类型成员”。 但我改变了主意 :我认为最后一句是指非静态类型成员(即嵌套类型),而不仅仅是非静态通用成员。

当然,如果没有从规范中引用这一点,就不会完成对4.8节的讨论:

原始类型的使用仅允许作为遗留代码兼容性的让步。 在将generics引入Java编程语言之后编写的代码中使用原始类型是非常不鼓励的。 未来版本的Java编程语言可能会禁止使用原始类型。

作为已接受答案的补充,如果您只是想尽可能简单地修复编译错误,并且您没有使用类的类型参数 ,那么最合适的修复(感谢@Tunaki)是

 ThingProducer thingProducer = new ThingProducer(); 

代替

 ThingProducer thingProducer = new ThingProducer(); 

这使我们处于generics世界,同时记录了类型参数的重要性。

(在这个缩减的例子中,改变ThingProducer会更有意义,但我怀疑在现实世界中,任何得到这个错误的人都可能正在处理他们无法改变的遗留类 – 至少我是这样的。)