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

Java 在原始类类型上忽略显式方法类型参数;编译器错误?,java,generics,language-lawyer,raw-types,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) {

我在调用带有显式类型参数的泛型方法时遇到一个编译器错误,好像没有考虑显式类型参数一样。最简单的例子:

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
    static
  • 声明
    thingProducer thingProducer
    而不是原始类型
    thingProducer
这是一个编译器错误吗?如果没有,JLS中的哪些规则定义了此行为?

回答了您的问题:

未从其超类或超接口继承的原始类型C的构造函数(§8.8)、实例方法(§8.4,§9.4)或非静态字段(§8.3)的类型是对应于在对应于C的泛型声明中删除其类型的原始类型

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

请注意,规范并没有说
getting()
,而是说
getting()
的类型是通过删除它所属的原始类型(即
ThingProducer
)构造的类型——它实际上是对
getting()
本身的删除,这意味着类型参数(
T
s
)都被删除了

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

当然,如果不引用规范中的以下内容,第4.8节的讨论就不完整:

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


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

这让我们置身于泛型的世界中,同时证明类型参数是什么并不重要


(在这个简化的示例中,更改ThingProducer更有意义,但我怀疑在现实世界中,任何出现此错误的人可能都在处理一个无法更改的遗留类——至少我是这样做的。)

ThingProducer ThingProducer
主要是一个原始类型。不好。例如,使用
ThingProducer ThingProducer
,您的代码编译得很好。这是一个关于原始类型的问题。重复问题的目的是要表明,永远不要使用原始类型,再次阅读已接受的答案。您不能执行基因ric对原始类型的调用。因此,您已经做对了,没有考虑显式类型参数。因为您正在对原始类型调用方法。关于它,没有什么要说的了,请不要使用原始类型。我认为您没有阅读代码示例。该类确实有一个类型参数,但我们没有引用类类型参数,但提供方法类型参数。根据我对JLS的理解,这应该是合法的。只有在得到答案后,我才设法找到重复的问题:谢谢你的澄清。我同意你的二读,粗体段落不适用于这里。但是您注意到,规范的措辞表明,原始
ThingProducer
上的
getThing()
返回类型
对象
(无论我在方法调用中输入了什么类型参数)。我们看到的编译错误反映了这一点。人们可能会认为方法类型参数应该“存活”删除类的类型参数,但编译器实现者已经拒绝了一个错误请求,请参阅:粗体的句子不适用。如果
thingA
声明为type
Object
,即使类型参数仍然传递:
Object thingA=thingProducer.getThing()
如果将
更改为
,错误也会消失。因此,错误不在方法调用本身,而是在随后的强制转换/赋值中,就好像方法的类型正在被删除一样。@Boann:同意。这就是我的编辑内容(请参阅“我改变主意了”一节)。粗体部分指的是类型成员(也就是说,嵌套类)不是普通的成员(比如这里的方法)。也就是说,我认为在我的答案中包含它不是错误的,是吗?@Andrewspuncer:“人们可能会期望”确实——正如你所期望的那样——但它不是真的,而且从来都不是。:-”正如所指出的,“能够使用原始类型(
Map
)“正确的做法是使用类型通配符(
ThingProducer
),而不是原始类型。@DanielPryden Yep,我刚刚找到了那个注释,并且
incompatible types: Object cannot be converted to ThingA
ThingProducer<?> thingProducer = new ThingProducer();
ThingProducer thingProducer = new ThingProducer();