Java泛型类型推断奇怪的行为?

Java泛型类型推断奇怪的行为?,java,generics,types,type-inference,type-erasure,Java,Generics,Types,Type Inference,Type Erasure,有人能向我解释一下这种行为吗: 注意:T从来没有在泛型中使用过 public static class SomeThingGeneric<T> { public List<String> getSomeList() { return null; } } final SomeThingGeneric<Object> someThingGenericObject = new SomeThing

有人能向我解释一下这种行为吗:

注意:T从来没有在泛型中使用过

   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!

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

代码可以分解为以下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?
}
公共静态类SomeThingGeneric{
公共列表getSomeList(){
返回null;
}
}
公共静态void main(最终字符串[]args){
final something generic something genericraw=new something generic();
对于(最后一个字符串s:someThingGenericRaw.getSomeList()){}//SAD compiler.WTF?
}
这导致编译器错误
类型不匹配:无法从元素类型对象转换为字符串

问题是:为什么会发生这种错误
T
不在
SomeThingGeneric
中的任何位置使用,尤其是在
getSomeList()方法的签名中。因此,当我们调用
getSomeList()
时,我们应该得到一个
列表。然而,我们显然得到了一个原始的
列表
类型


原因是类型擦除。从:

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

他说:

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


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

好吧,这是一个有趣的问题,尽管有反对票。我相信问题的答案就在这部分:

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


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

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

将整个类标记为擦除,不编译定义非常好的泛型方法,是不好的


请报告它。

回答:当您将泛型类型实例化为原始类型时,它的所有类型实例化都会被删除,到处都是原始类型(即使它们与类型参数无关,例如Q中的t)。i、 e.原始类型100%为“无仿制药”


原因:与前泛型代码向后兼容

但请注意,我并没有在任何地方使用T。一般来说,这一点很好——一开始我并没有意识到这一点。。。让我们看看。。。(顺便说一句,这就是为什么在问题中稍微描述一下这个问题是有用的;)我没有投反对票;)您是否检查了早期的编译器版本(1.6/1.5)?在1.6和1.5中都失败。未检查1.7+也会在1.7(Eclipse)中失败。我想这可能与类型擦除有关,但还没有找到一个参考…我想不出或在任何文本中都没有找到特别好的理由。如果字段/方法被类型化为类的泛型参数,则(显然)有意义。可能是为了实现与遗留代码的某种兼容性。尽管如此,行为记录在JLS、fwiw中。尽管如此,问题和答案都适用于此处。(特别是,答案是“这样做是为了向后兼容,这不应该是一个问题,因为你不应该在新代码中使用原始类型。”@wrick
正在从可信和/或官方来源寻找答案。
-除了JLS的引用之外,你还想看到什么?这不是一个bug。它在JLS中有很好的文档记录。方法的签名定义得很好,所以我仍然看不到它背后的逻辑被拒绝。逻辑是:不要使用原始类型。它们仅出于兼容性原因提供。请参阅@Louis在其评论中的链接。对,但不要编译erasure类的任何not erasure方法,这一定是不可接受的,因为代码行使用非常通用的定义方法,并且仍然兼容,因为原始行为始终存在。在设计语言时,应记住以下几点:
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?
}