在原始类类型上忽略显式方法类型参数;编译bug?

时间:2015-11-16 12:53:29

标签: java generics language-lawyer raw-types

我在使用显式类型参数调用泛型方法时遇到编译器错误,就好像未考虑显式类型参数一样。最小的例子:

class CastExample {
    static class ThingProducer<S> {
        public <T> T getThing() { return null; }
    }

    static class ThingA {}

    public static void main(String... args) {
        ThingProducer thingProducer = new ThingProducer();
        ThingA thingA = thingProducer.<ThingA>getThing(); // compile error here
    }
}

ThingProducer是一个原始类型,因为该类有一个类型参数,但在调用getThing时,我们没有引用类型参数,而是提供方法类型参数。根据我对JLS的理解,这应该是合法的,但它给了我这个错误:

incompatible types: Object cannot be converted to ThingA

如果我

,错误就会消失
  • <S>
  • 中删除ThingProducer
  • 或制作getThing静态
  • 声明thingProducer ThingProducer<?>而不是原始类型ThingProducer

这是编译器错误吗?如果不是,JLS中的哪个规则定义了这种行为?

2 个答案:

答案 0 :(得分:1)

Section 4.8 of the Java Language Specification回答了您的问题:

  

未从其超类或超接口继承的原始类型C的构造函数(第8.8节),实例方法(第8.4节,第9.4节)或非静态字段(第8.3节)的类型是原始类型对应于在与C相对应的通用声明中擦除其类型。

在您的示例中,getThing()是“原始类型C的实例方法... [在这种情况下,ThingProducer],它不是继承的”。根据JLS,它的类型是“与通用声明中类型的擦除相对应的原始类型”。在getThing()的通用声明中,其类型T是无界的,这意味着它的删除是java.lang.Object

请注意,规范表示getThing()的类型是通过删除其所属的原始类型构建的类型(即ThingProducer ) - 它实际上是getThing()本身的擦除,这意味着两个类型参数(TS)都被删除。

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

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

  

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

答案 1 :(得分:1)

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

ThingProducer<?> thingProducer = new ThingProducer();

而不是

ThingProducer thingProducer = new ThingProducer();

让我们进入仿制药的世界,同时记录了类型参数的含义并不重要。

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