【问题标题】:How DWR cast incoming data and evade type erasureDWR 如何投射传入数据并避免类型擦除
【发布时间】:2016-10-14 08:38:02
【问题描述】:

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

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

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

假设我们有类似的东西:

public String foo(List<String> possibleFoos) {

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

【问题讨论】:

    标签: java reflection collections type-erasure dwr


    【解决方案1】:

    虽然 Java 确实会在运行时擦除类型(因此将 List&lt;String&gt; 变成了 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,但针对泛型类型)、解析特定类型变量等。

    【讨论】:

    • 感谢您的全面回答,这正是我想知道的!
    • 我使用了“麻烦”版本,它适用于列表,但不适用于地图,因为 getParameterizedType 返回类对象。由于我对您之前的帖子有了更好的了解,因此我尝试再次搜索如何使用地图进行操作,但没有找到任何东西。你能再帮我一次吗?
    • @Radim 我已经更新了我的答案,以包含一个将 Map 作为第二个参数的示例。这就是你所追求的吗?
    • 不幸的是没有。问题是我正在使用类型参数。我对列表所做的是 (ParametrizedType)parameter.getParameterizedType。但是 getParameterizedType 返回一个 Class 对象而不是 Map 的 Type。我正在使用它,因为我正在迭代匹配方法的参数。
    • 不能直接使用参数。您必须从 getActualTypeArguments 中按索引获取返回类型。无论如何,很高兴你解决了它。
    猜你喜欢
    • 2018-06-18
    • 2012-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多