在不丢失类型信息的情况下获取 class 列表的 class 对象的正确方法是什么?

What is the correct way to obtain the class object for class List without losing type information?

作为我的问题的前言,请注意我知道什么是擦除类型。我觉得有必要说明这一点,这样有用的答案就不会被基本解释所淹没。

我想获取 class 列表的 class 对象。对于非泛型类型,我会使用像 String.class 这样的 class 文字,但对于 List,即使我可以获得正确的行为,我仍然会收到警告,说我做错了。

据我了解,List 的 class 对象属于 Class<List> 类型,但即使写下该类型,

Class<List> c = null;

发出警告:"Raw use of parameterized class 'List'"。由于类型擦除,class 对象无法了解列表元素,所以我的下一个猜测是

Class<List<?>> c = null;

作为一种类型被接受。接下来我必须获得 class 对象,但是尝试写下 class 文字的每种方法都失败了:

Class<List<?>> l = List.class;

给出硬类型错误,而

Class<List<?>> l = List<?>.class;

说 "Cannot select from parameterized type"。

Class<List<?>> l = (Class<List>)List.class;

失败并出现与没有强制转换时相同的硬类型错误(由于强制转换是多余的,这是预期的),并且

Class<List<?>> l = (Class<List<?>>)List.class;

说 "inconvertible types"。

接下来,我尝试了以下无效代码片段以从编译器错误中学习一些东西:

List<?> l = new ArrayList<>();
int x = l.getClass();

现在编译器给出 Class<? extends java.util.List> 的类型错误 -- 暂时忽略 "extends" 部分,这是显而易见的,因为 getClass() 可以 return 一个子class,这告诉我 .getClass() 只想谈论原始 List 类型,而不是 List<?> 或类似的。在某种程度上,这对我来说似乎是正确的,因为 class 对象代表任何 List 实例,而不是 "lists whose type is unknown"。但是,我现在回到原点,因为只写下原始类型会发出警告,所以我显然不希望这样做(我知道原始类型可用于处理遗留代码,但我在这里不这样做)。

我知道我可以像这样丢弃有关 class 对象的所有静态类型信息:

Class<?> c = List.class;

但这通常是不可行的,事实上,我正在尝试解决一个需要该类型信息的更大问题。

虽然我知道我可以轻松禁用警告或使用类似的肮脏技巧,但我想知道获取 List 的 class 对象的正确方法是什么。

你不可能赢得这场 war。

没有 Class<List<?>> 的实例之类的东西。这是一个有效的 type,但是没有具体的值可以分配给它(null 除外),因为 List.classnew ArrayList<>().getClass() 有分别输入 Class<List>Class<? extends List>

Class 实例本质上是原始类型的,因为类型擦除。

可以进行未经检查的转换:

Class<List<?>> clazz = (Class<List<?>>) (Class<?>) List.class;

但这也会产生 warnings;并且由于您可以进行其他不安全的转换,例如:

Class<List<?>> clazz1 = (Class<List<?>>) (Class<?>) ArrayList.class;

你可能会遇到一种奇怪的情况,即 Class<List<?>> 的两个实例不相等,或者例如有时 Class<List<?>> 可以被实例化(使用 clazz.newInstance()),而其他有时不会。


现在,如果您需要某种通用类型的标记,您可以使用类似 Guice 的方法:

TypeLiteral<ArrayList<?>> typeLiteral =
    new TypeLiteral<ArrayList<?>>() {};

由于捕获超类的方式, 可能在运行时从中获得 ArrayList<?>(注意 {} - 这是TypeLiteral 的匿名子类)。如果你不想依赖 Guice(或其他提供类似结构的库),你可以很容易地自己实现它,使用 typeLiteral.getClass().getGenericSuperclass().

但这是否是您可以采用的方法取决于您未公开的问题。关键要点是泛型和反射不能很好地结合在一起。