简体   繁体   English

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

[英]Java Generic Type Inference Strange Behavior?

Can someone explain this behaviour to me: 有人可以向我解释这种行为:

Note: That T is never used in SomeThingGeneric 注意: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) and (2) compiles but (3) fails with following message: (1)和(2)编译,但(3)失败,并带有以下信息:

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

If anyone wants the full code, here it is . 如果有人想要完整的代码,就在这里 I have verified this in both Java 5 and 6. 我已经在Java 5和6中验证了这一点。

Well, this is an interesting question despite the downvotes. 嗯,这是一个有趣的问题,尽管这是一个有趣的问题。 I believe the answer to the question lies in this portion of the JLS : 我相信这个问题的答案在于JLS的这一部分:

The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C. 未从其超类或超接口继承的原始类型C的构造函数(第8.8节), 实例方法 (第8.4节,第9.4节)或非静态字段(第8.3节)M的类型是对应的原始类型在对应于C的通用声明中擦除其类型

Effectively, your method public List<String> getSomeList gets an effective signature of public List getSomeList , in the scenario where you accessing it via a raw type. 实际上,您的方法public List<String> getSomeList在通过原始类型访问它的场景中获取public List getSomeList的有效签名。 And as such, the list iterator for the resulting list then 'returns' objects instead of strings. 因此,结果列表的列表迭代器然后“返回”对象而不是字符串。

The code can be broken down to the following SSCCE: 代码可以分解为以下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?
}

This results in the compiler error Type mismatch: cannot convert from element type Object to String . 这导致编译器错误Type mismatch: cannot convert from element type Object to String

The question is: why does this error occur? 问题是:为什么会出现这种错误? T is not used anywhere in SomeThingGeneric , and especially not in the signature of the getSomeList() method. T不会在SomeThingGeneric任何位置使用,尤其不会在getSomeList()方法的签名中使用。 So, when we call getSomeList() , we should get a List<String> . 所以,当我们调用getSomeList() ,我们应该得到一个List<String> However, we obviously get a raw List type. 但是,我们显然得到了一个原始的List类型。


The reason is type erasure. 原因是类型擦除。 From the JLS : 来自JLS

Type erasure also maps the signature (§8.4.2) of a constructor or method to a signature that has no parameterized types or type variables. 类型擦除还将构造函数或方法的签名(第8.4.2节)映射到没有参数化类型或类型变量的签名

And, the definition of a raw type says: 并且, raw type的定义说:

To facilitate interfacing with non-generic legacy code, it is possible to use as a type the erasure (§4.6) of a parameterized type (§4.5) or the erasure of an array type (§10.1) whose element type is a parameterized type. 为了便于与非泛型遗留代码接口,可以使用参数化类型 (第4.5节)的擦除(第4.6节 )或者数组类型为参数化类型的数组类型(第10.1节)的擦除作为类型。 。 Such a type is called a raw type . 这种类型称为原始类型

So, when using a raw type , it is the generic type with type erasure applied. 因此,在使用原始类型时 ,它是应用了类型擦除的泛型类型 When applying type erasure, all method signatures are mapped to types with no type parameters or type variables. 应用类型擦除时,所有方法签名都映射到没有类型参数或类型变量的类型。 Hence, the method List<String> getSomeList() from the example becomes List getSomeList() which results in the shown compiler error. 因此,示例中的方法List<String> getSomeList()变为List getSomeList() ,这导致显示的编译器错误。

Answer: When you instantiate a generic type as a raw type, ALL of it's type instantiations are removed, leaving raw types everywhere (even if they don't relate to Type parameters, such as T in your Q). 答案:当您将泛型类型实例化为原始类型时,将删除其所有类型实例化,从而在任何地方保留原始类型(即使它们与类型参数无关,例如Q中的T)。 ie raw types are 100% "generics-free". 即原始类型是100%“无泛型”。

Reason: backwards compatability with pre-generic code. 原因:与预通用代码向后兼容。

For my opinion is a bug. 我的意见是一个错误。 Because it must work. 因为它必须工作。 The signature of the method do not use the generic T type. 该方法的签名不使用通用T类型。 So it must compile. 所以它必须编译。

Mark the entire class as erasure, do not compiling the very well defined generic methods, is not good. 将整个类标记为擦除,不编译非常好的通用方法,并不好。

Please report it. 请报告。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM