在不丢失类型信息的情况下获取 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.class
和 new 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()
.
但这是否是您可以采用的方法取决于您未公开的问题。关键要点是泛型和反射不能很好地结合在一起。
作为我的问题的前言,请注意我知道什么是擦除类型。我觉得有必要说明这一点,这样有用的答案就不会被基本解释所淹没。
我想获取 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.class
和 new 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()
.
但这是否是您可以采用的方法取决于您未公开的问题。关键要点是泛型和反射不能很好地结合在一起。