我有以下代码:
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")
。我的问题如下:
是否记录了这种行为?
这种奇怪行为的原因是什么?
答案 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解释的那样,完全省略类型修饰符也会禁用与类类型不直接相关的其他泛型声明。