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

And*_*cer 5 java generics raw-types language-lawyer

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

incompatible types: Object cannot be converted to ThingA
Run Code Online (Sandbox Code Playgroud)

如果我,错误就会消失

  • 除去<S>ThingProducer
  • 或使getThing静态
  • 声明thingProducer ThingProducer<?>而不是原始类型ThingProducer

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

Dan*_*den 3

Java 语言规范第 4.8 节回答了您的问题:

\n\n
\n

原始类型 C 的构造函数 (\xc2\xa78.8)、实例方法 (\xc2\xa78.4、\xc2\xa79.4) 或非静态字段 (\xc2\xa78.3) 的类型不是从其超类或超接口继承的是原始类型,对应于 C 对应的泛型声明中其类型的擦除。

\n
\n\n

在您的示例中,getThing()是“原始类型 C 的实例方法……[在本例中,ThingProducer] 不是继承的”。根据 JLS 的说法,它的类型是“对应于泛型声明中其类型擦除的原始类型”。在泛型声明中,getThing()其类型T是无界的,这意味着它的擦除是java.lang.Object

\n\n

请注意,规范并没有\getThing()的类型是通过擦除它所属的原始类型(即ThingProducer)而构造的类型 - 它实际上是擦除其getThing()自身,这意味着类型参数(TS)被删除。

\n\n

[旁白:在我原来的答案中,我引用了规范的另一句话:“将类型参数传递给不是从其超类或超接口继承的原始类型的非静态类型成员是一个编译时错误。” 我对这句话的最初解读是,编译器需要上面的语法发出编译时错误,因为我得出的结论是,您试图“将类型参数传递给原始类型的非静态类型成员”。但我改变了主意:我相信最后一句指的是非静态类型成员(即嵌套类型),而不仅仅是非静态泛型成员。]

\n\n

当然,如果不引用规范中的这一点,第 4.8 节的讨论就不完整:

\n\n
\n

仅允许使用原始类型作为对遗留代码兼容性的让步。强烈建议不要在 Java 编程语言中引入泛型之后编写的代码中使用原始类型。Java 编程语言的未来版本可能会禁止使用原始类型。

\n
\n