DWR如何转换传入数据并规避类型擦除

时间:2016-10-14 08:38:02

标签: java reflection collections type-erasure dwr

我想请求一个项目集合(特定反射的种类)。但关于类型擦除似乎是不可能的,也是关于我在堆栈上阅读的一些主题。有一些解决方法(here),但我很好奇,如果有人知道它是如何完成的,例如DWR:

http://directwebremoting.org/dwr/documentation/server/configuration/dwrxml/signatures.html

或者如果有更好的解决方法,那就太棒了。

我们说我们有类似的东西:

public String foo(List<String> possibleFoos) {

我需要的是找出参数possibleFoos是字符串列表,而不仅仅是List

1 个答案:

答案 0 :(得分:6)

虽然Java会在运行时擦除类型(因此将List<String>转换为List),但在很多情况下它实际上会在运行时存储泛型类型,允许您恢复擦除信息丢失了。 您可以检索这些的实际泛型类型:

  • 方法参数(您的情况)
  • 方法返回类型
  • 字段类型
  • 超级班级/接口

这意味着如果你只是一个List类型的对象,那么你无法做到这一点来获得它的通用类型(object.getClass()会得到你List而且#&# 39;它) - 它已经永久丢失了。但是,如果您正在尝试找出方法参数或上述任何一种的泛型类型,那么通常使用反射可以。由于您的案例不涉及类型变量或其他复杂情况,因此获得实际类型非常简单:

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的精彩库,为您完成艰苦的工作,如果您需要AnnotatedType的支持,您可以使用我的分叉叫GeAnTyRef(两者都在Maven Central)。它们还包括一个类型工厂,它允许您构造(Annotated)Type的实例,使用普通的Java API无法轻松完成。还有一个方便的super type token实现,允许您获得(Annotated)Type字面值。

有了这些,您可以使用Java允许的泛型类型执行所有操作,而无需我上面解释过的麻烦:

  • GenericTypeReflector#getExactParameterTypes( ... )
  • GenericTypeReflector#getExactReturnType( ... )
  • GenericTypeReflector#getExactFieldType( ... )
  • GenericTypeReflector#getExactSuperType( ... )

还有更多的操作,比如确定一个Type是否是另一个的超类型(类似于Class#isAssignableFrom但是对于泛型类型),解析特定类型变量等。