我在使用显式类型参数调用泛型方法时遇到编译器错误,就好像未考虑显式类型参数一样。最小的例子:
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中的哪个规则定义了这种行为?
答案 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()
本身的擦除,这意味着两个类型参数(T
和S
)都被删除。
[旁白:在我的原始答案中,我引用了规范的另一句话:“将类型参数传递给原始类型的非静态类型成员是一个编译时错误,该成员不是从其超类继承的,也不是超级“。我对该句子的原始读取是,编译器 required 为上面的语法发出编译时错误,因为我断定你试图“将类型参数传递给非静态类型成员原始类型“。但我改变了主意:我认为最后一句是指非静态类型成员(即嵌套类型),而不仅仅是非静态通用成员。]
当然,如果没有引用规范中的这一位,就不会完成对4.8节的讨论:
原始类型的使用仅允许作为遗留代码兼容性的让步。在将泛型引入Java编程语言之后编写的代码中使用原始类型是非常不鼓励的。未来版本的Java编程语言可能会禁止使用原始类型。
答案 1 :(得分:1)
作为已接受答案的补充,如果您只想尽可能简单地修复编译错误,并且您没有使用类的参数<S>
,最合适的解决方法(感谢@Tunaki)是
ThingProducer<?> thingProducer = new ThingProducer();
而不是
ThingProducer thingProducer = new ThingProducer();
让我们进入仿制药的世界,同时记录了类型参数的含义并不重要。
(在这个缩减的例子中,改变ThingProducer会更有意义,但我怀疑在现实世界中,任何得到这个错误的人都可能正在处理他们无法改变的遗留类 - 这就是我的情况至少。)