通常,我看到人们这样使用类文字:

Class<Foo> cls = Foo.class;

但是如果类型是泛型的,比如List呢?这很好,但有一个警告,因为List应该参数化:

Class<List> cls = List.class

那么为什么不添加一个<?>?这将导致类型不匹配错误:

Class<List<?>> cls = List.class

我想这样做是可行的,但这只是一个简单的语法错误:

Class<List<Foo>> cls = List<Foo>.class

我怎么能得到一个类<列表<Foo>>静态,例如使用类文字?

在第一个例子Class<List> cls = List. Class中,我可以使用@SuppressWarnings(“unchecked”)来消除因非参数化使用List而引起的警告,但我宁愿不这样做。

有什么建议吗?


当前回答

你不能由于类型擦除。

Java泛型只不过是对象强制转换的语法糖。为了演示:

List<Integer> list1 = new ArrayList<Integer>();
List<String> list2 = (List<String>)list1;
list2.add("foo"); // perfectly legal

在运行时保留泛型类型信息的唯一实例是通过反射询问类的成员时使用Field.getGenericType()。

这就是Object.getClass()有这个签名的原因:

public final native Class<?> getClass();

重要的部分是Class<?>。

换句话说,摘自Java泛型常见问题解答:

Why is there no class literal for concrete parameterized types? Because parameterized type has no exact runtime type representation. A class literal denotes a Class object that represents a given type. For instance, the class literal String.class denotes the Class object that represents the type String and is identical to the Class object that is returned when method getClass is invoked on a String object. A class literal can be used for runtime type checks and for reflection. Parameterized types lose their type arguments when they are translated to byte code during compilation in a process called type erasure . As a side effect of type erasure, all instantiations of a generic type share the same runtime representation, namely that of the corresponding raw type . In other words, parameterized types do not have type representation of their own. Consequently, there is no point in forming class literals such as List<String>.class , List<Long>.class and List<?>.class , since no such Class objects exist. Only the raw type List has a Class object that represents its runtime type. It is referred to as List.class.

其他回答

为了阐明cletus的答案,在运行时删除所有泛型类型的记录。泛型只在编译器中处理,用于提供额外的类型安全。它们实际上只是允许编译器在适当的位置插入类型转换的简写。例如,以前你必须做以下事情:

List x = new ArrayList();
x.add(new SomeClass());
Iterator i = x.iterator();
SomeClass z = (SomeClass) i.next();

就变成了

List<SomeClass> x = new ArrayList<SomeClass>();
x.add(new SomeClass());
Iterator<SomeClass> i = x.iterator();
SomeClass z = i.next();

这允许编译器在编译时检查代码,但在运行时它看起来仍然像第一个示例。

你不能由于类型擦除。

Java泛型只不过是对象强制转换的语法糖。为了演示:

List<Integer> list1 = new ArrayList<Integer>();
List<String> list2 = (List<String>)list1;
list2.add("foo"); // perfectly legal

在运行时保留泛型类型信息的唯一实例是通过反射询问类的成员时使用Field.getGenericType()。

这就是Object.getClass()有这个签名的原因:

public final native Class<?> getClass();

重要的部分是Class<?>。

换句话说,摘自Java泛型常见问题解答:

Why is there no class literal for concrete parameterized types? Because parameterized type has no exact runtime type representation. A class literal denotes a Class object that represents a given type. For instance, the class literal String.class denotes the Class object that represents the type String and is identical to the Class object that is returned when method getClass is invoked on a String object. A class literal can be used for runtime type checks and for reflection. Parameterized types lose their type arguments when they are translated to byte code during compilation in a process called type erasure . As a side effect of type erasure, all instantiations of a generic type share the same runtime representation, namely that of the corresponding raw type . In other words, parameterized types do not have type representation of their own. Consequently, there is no point in forming class literals such as List<String>.class , List<Long>.class and List<?>.class , since no such Class objects exist. Only the raw type List has a Class object that represents its runtime type. It is referred to as List.class.

由于暴露的事实是Class字面量没有泛型类型信息,我认为您应该假定不可能消除所有警告。在某种程度上,使用Class<Something>与使用不指定泛型类型的集合相同。我能想到的最好的结果是:

private <C extends A<C>> List<C> getList(Class<C> cls) {
    List<C> res = new ArrayList<C>();
    // "snip"... some stuff happening in here, using cls
    return res;
}

public <C extends A<C>> List<A<C>> getList() {
    return getList(A.class);
}

Java泛型常见问题解答和cletus的回答听起来好像Class<List<T>>是没有意义的,但真正的问题是这是极其危险的:

@SuppressWarnings("unchecked")
Class<List<String>> stringListClass = (Class<List<String>>) (Class<?>) List.class;

List<Integer> intList = new ArrayList<>();
intList.add(1);
List<String> stringList = stringListClass.cast(intList);
// Surprise!
String firstElement = stringList.get(0);

cast()使它看起来似乎是安全的,但实际上它一点也不安全。


虽然我不知道哪里不能有List<?>.class = Class<List<?>>,因为当你有一个基于Class参数的泛型类型确定类型的方法时,这将是非常有用的。

对于getClass(), JDK-6184881请求切换到使用通配符,但是看起来不会执行这个更改(很快),因为它与前面的代码不兼容(请参阅注释)。

你可以使用双重强制转换:

@SuppressWarnings(“未选中”) 类<List> <Foo>cls = (类<列表<Foo>>)(对象)列表.class