Java泛型是一个全有或全无的决定吗?

时间:2014-10-24 16:09:52

标签: java generics

我有以下代码:

public class Main {
    public static void main(String[] args) {
        Generic generic = new Generic<Integer>(5);
        List<String> stringList = generic.getStringList(); // this line is where the compiler complains
    }
}

public class Generic<T> {
    private T member;

    public Generic(T member) {
        this.member = member;
    }

    public T getMember() {
        return member;
    }

    public List<String> getStringList() {
        return new ArrayList<String>();
    }
}

请注意,使用泛型类型参数声明类Generic,但方法generic中的变量main属于擦除类型,即。即没有类型参数。我不明白的是为什么编译器抱怨分配List<String>

的行
Warning:(6, 56) java: unchecked conversion
  required: java.util.List<java.lang.String>
  found:    java.util.List

该方法明确返回List<String>,与该类的泛型参数无关。这就是变量stringList所期望的。似乎在类级别上不使用变量generic的泛型参数会关闭所有泛型处理,而不仅仅是取决于类的类型参数。

我正在使用标准的Oracle Java 1.7.0_55编译器,如果这很重要的话。

我不是在问如何摆脱警告。我知道我应该将变量类型声明为Generic<Integer>,或者可以使用@SuppressWarnings("unchecked")。我的问题如下:

是否记录了这种行为?

这种奇怪行为的原因是什么?

3 个答案:

答案 0 :(得分:13)

当您使用类型的擦除时,它会删除泛型的所有跟踪 - 而不仅仅是类型参数T的使用。因此,您的generic变量就像它指的是这种类型:

// After type erasure
public class Generic {
    private Object member;

    public Generic(Object member) {
        this.member = member;
    }

    public Object getMember() {
        return member;
    }

    public List getStringList() {
        return new ArrayList();
    }
}

这在JLS中有记录 - 从section 4.6开始并按照链接进行操作。它并不像它可能那样清晰,但 已记录在案。

原因是,如果您使用的是原始类型,编译器希望您根本不了解泛型 - 因为它可能正在编译传统的Java-5之前的代码。随着时间的推移,这被证明有点不切实际,但我相信这是规范的动机。

答案 1 :(得分:4)

第一个问题的答案是here

特别是行:

  

类型擦除还映射构造函数的签名(第8.4.2节)或   没有参数化类型或类型的签名的方法   变量。构造函数或方法签名的擦除是a   签名由与s相同的名称和所有的擦除组成   s中给出的形式参数类型。

     

构造函数或方法的类型参数(第8.4.4节)和   返回类型(§8.4.5)的一种方法,如果是,也会进行擦除   构造函数或方法的签名被删除。

     

删除泛型方法的签名没有类型   参数。

至于原因,我的答案有些“手绘”。基本上,Java的类型系统比现在更弱,而且Generics公开了一些导致令人头疼的低级实现细节。具体来说,每次将项插入数组时,都会在运行时进行类型检查。 Java arrays are covariant.由于在运行时进行了类型检查,并且规范中定义了擦除行为,编译器希望警告您应该知道可能存在的问题。

或多或少,这是一个设计决定,在Generics实施后引起了很多麻烦。

答案 2 :(得分:3)

除了Jon的回答之外,您可能想要做的是声明您的变量仍然使用泛型,但接受任何类型:

Generic<?> generic = ...;

如Jon解释的那样,完全省略类型修饰符也会禁用与类类型不直接相关的其他泛型声明。