通常,我看到人们这样使用类文字:
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泛型常见问题解答和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请求切换到使用通配符,但是看起来不会执行这个更改(很快),因为它与前面的代码不兼容(请参阅注释)。
参数化类型没有Class字面量,但是有正确定义这些类型的Type对象。
看到java.lang.reflect.ParameterizedType
——http://java.sun.com/j2se/1.5.0/docs/api/java/lang/reflect/ParameterizedType.html
谷歌的Gson库定义了一个TypeToken类,允许简单地生成参数化类型,并使用它以通用友好的方式规范具有复杂参数化类型的json对象。在你的例子中,你可以用:
Type typeOfListOfFoo = new TypeToken<List<Foo>>(){}.getType()
我打算发布到TypeToken和Gson类javadoc的链接,但堆栈溢出不会让我发布多个链接,因为我是一个新用户,你可以很容易地使用谷歌搜索找到它们
为了阐明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.