Java通用类型推断奇怪的行为?

时间:2013-04-09 07:19:38

标签: java generics types type-inference type-erasure

有人可以向我解释这种行为:

注意:T从未在SomeThingGeneric中使用

   public static class SomeThingGeneric<T> {
        public List<String> getSomeList() {
            return null;
        }
    }

final SomeThingGeneric<Object> someThingGenericObject = new SomeThingGeneric<Object>();
final SomeThingGeneric<?> someThingGenericWildcard    = new SomeThingGeneric<Object>();
final SomeThingGeneric someThingGenericRaw            = new SomeThingGeneric<Object>();

for (final String s : someThingGenericObject.getSomeList()) { }   // 1 - compiles
for (final String s : someThingGenericWildcard.getSomeList()) { } // 2 - compiles
for (final String s : someThingGenericRaw.getSomeList()) { }      // 3 - does not compile!

(1)和(2)编译,但(3)失败,并带有以下消息:

incompatible types
found   : java.lang.Object
required: java.lang.String

如果有人想要完整的代码,here it is。我已经在Java 5和6中验证了这一点。

4 个答案:

答案 0 :(得分:4)

嗯,这是一个有趣的问题,尽管这是一个有趣的问题。我相信这个问题的答案在于JLS的这一部分:

  

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

实际上,您的方法public List<String> getSomeList会在您通过原始类型访问它的方案中获得public List getSomeList的有效签名。因此,结果列表的列表迭代器然后“返回”对象而不是字符串。

答案 1 :(得分:3)

代码可以分解为以下SSCCE:

public static class SomeThingGeneric<T> {
    public List<String> getSomeList() {
        return null;
    }
}

public static void main(final String[] args) {
    final SomeThingGeneric someThingGenericRaw = new SomeThingGeneric<Object>();
    for (final String s : someThingGenericRaw.getSomeList()) { }        //  SAD compiler.  WTF?
}

这会导致编译器错误Type mismatch: cannot convert from element type Object to String

问题是:为什么会出现这种错误? T中的任何位置都未使用SomeThingGeneric,尤其是getSomeList()方法的签名中未使用getSomeList()。因此,当我们致电List<String>时,我们应该获得List。但是,我们显然得到了原始的List<String> getSomeList()类型。


原因是类型擦除。来自JLS

  

类型擦除还会将构造函数的签名(第8.4.2节)或方法映射到没有参数化类型或类型变量的签名。

并且,the definition of a raw type说:

  

为了便于与非泛型遗留代码接口,可以使用参数化类型的擦除(§4.6)(§4.5)或数组类型的擦除( §10.1)其元素类型是参数化类型。 这种类型称为原始类型

因此,在使用原始类型时,应用了类型擦除的泛型类型。应用类型擦除时,所有方法签名都映射到没有类型参数或类型变量的类型。因此,示例中的方法List getSomeList()变为{{1}},导致显示的编译器错误。

答案 2 :(得分:3)

答案:当您将泛型类型实例化为原始类型时,将删除其所有类型实例化,从而将原始类型保留在任何位置(即使它们与类型参数无关,例如Q中的T)。即原始类型是100%“无泛型”。

原因:与通用前代码的向后兼容性。

答案 3 :(得分:0)

我认为这是一个错误。因为它必须工作。该方法的签名不使用通用T类型。所以它必须编译。

将整个类标记为擦除,不编译非常好的通用方法,不好。

请报告。