虽然 Java 确实会在运行时擦除类型(因此将 List<String> 变成了 List),但在许多情况下,它实际上确实在运行时存储了泛型类型,从而允许您恢复因擦除而丢失的信息。
您可以检索这些的实际泛型类型:
- 方法参数(您的情况)
- 方法返回类型
- 字段类型
- 超类/接口
这意味着,如果您只是拥有一个 List 类型的对象,那么您将无法获得它的泛型类型(object.getClass() 将让您获得List 就是这样) - 它已经永久丢失了。但是,如果您试图找出方法参数或上述任何一种的泛型类型,您通常可以使用反射。由于您的案例不涉及类型变量或其他复杂情况,因此获取实际类型非常简单:
ParameterizedType listArgument = (ParameterizedType) ClassDeclaringFoo.class.getMethod("foo", List.class).getGenericParameterTypes()[0];
Type listType = listArgument.getActualTypeArguments()[0]; //This will be a Class representing String, the type of the List
如果您有更多参数和地图:
public String foo(Object bar, Map<String, Number> possibleFoos) { ... }
代码类似:
ParameterizedType mapArgument = (ParameterizedType) ClassDeclaringFoo.class.getMethod("foo", Object.class, Map.class).getGenericParameterTypes()[1]; //1 is the index of the map argument
Type keyType = mapArgument.getActualTypeArguments()[0]; //This will be a Class representing String, the type of the Map key
Type valType = mapArgument.getActualTypeArguments()[1]; //This will be a Class representing Number, the type of the Map value
可以肯定地假设这也是 DWR 所使用的,因为类型是方法参数。
类似的方法可用于其他列出的案例:
-
Class.getMethod(...).getGenericReturnType() 将为您提供真正的返回类型
-
Class.getField(fieldName).getGenericType() 将为您提供该字段的真实类型
-
Class.getGenericSuperClass() 会让你成为真正的超级类型
-
Class.getGenericInterfaces() 将为您提供真正的接口类型
存在允许访问 Java 8 中引入的AnnotatedType(泛型类型加上类型使用注释)的等效方法:
Class.getMethod(...).getAnnotatedParameterTypes()
Class.getMethod(...).getAnnotatedReturnType()
Class.getField(fieldName).getAnnotatedType()
Class.getAnnotatedSuperClass()
Class.getAnnotatedInterfaces()
现在,当您的案例像示例中那样简单时,这一切都变得很花哨。
但是,想象一下,如果您的示例如下所示:
public T foo(List<T> possibleFoos) {...}
在这种情况下,getGenericParameterTypes()[0].getActualTypeArguments()[0] 会给你T,这是相当没用的。要解决 T 的含义,您必须查看类定义,也许还有超类,同时跟踪每个类中类型变量的命名方式,因为每个类中的名称可能不同。
为了更轻松地使用泛型类型反射,您可以使用一个名为 GenTyRef 的出色库来为您完成这项艰巨的工作,如果您需要对 AnnotatedTypes 的支持,您可以使用我的名为 GeAnTyRef 的 fork (两者都在 Maven 中心)。它们还包括一个类型工厂,它允许您构造 (Annotated)Type 的实例,而使用普通 Java API 则无法轻松做到这一点。还有一个方便的 super type token 实现,可让您获得 (Annotated)Type 文字。
有了这些,您可以使用 Java 允许的泛型类型来做所有事情,而无需我在上面解释的麻烦:
GenericTypeReflector#getExactParameterTypes( ... )
GenericTypeReflector#getExactReturnType( ... )
GenericTypeReflector#getExactFieldType( ... )
GenericTypeReflector#getExactSuperType( ... )
还有更多操作,例如判断一个 Type 是否是另一个的超类型(类似于 Class#isAssignableFrom,但针对泛型类型)、解析特定类型变量等。