【问题标题】:Getting around Type Erasure in Java在 Java 中绕过类型擦除
【发布时间】:2023-03-31 11:04:01
【问题描述】:

因此,与我合作的小组减少了我们必须为某些事情输入的代码量。在这种情况下,使用 DisplayTag 库显示列表的 Spring 网页。它的完成方式是使用泛型扩展Controller 对象的类,然后为它应该处理的每个页面创建一个子类。

此控制器显示SystemErrorReport,并将类型定义为SystemErrorReportCommand

public class SystemErrorReportController extends
    GenericSearchAndSelectController<SystemErrorReportCommand> {

问题在于 SystemErrorReportCommand 作为类型传递,需要在其构造函数中手动声明自身,如下所示:

public SystemErrorReportCommand()
{
    super(SystemErrorReport.class);
}

命令对象稍后被传递给需要知道其显式类型的东西。如果没有在某处手动指定它,它会以GenericCommand 的形式返回,这是我们的另一个类,因为在编译时间后SystemErrorReportCommand 位会丢失。

我对此并不感到兴奋,因为我们似乎可以进一步自动化以减少开发人员错误。有没有更优雅的方法来做到这一点,还是因为类型擦除而我坚持这样做?

【问题讨论】:

    标签: java generics type-erasure


    【解决方案1】:

    与被广泛接受且鲜为人知的类型相反,可以避免类型擦除,这意味着被调用者确实有能力知道在调用期间使用了哪些泛型参数。

    请看: Using TypeTokens to retrieve generic parameters

    谢谢

    【讨论】:

      【解决方案2】:

      即使在擦除完成后,仍然可以从以下位置检索通用信息:字段、方法(包括方法参数)和使用反射的类。

      例如,可以使用

      从 SystemErrorReportController 检索 SystemErrorReportCommand
      ((ParameterizedType) SystemErrorReportController.class.getGenericSuperType()).getActualTypeArguments[0];
      

      不过,这只是开始。您需要开始使用反射 api 来利用这些信息,因此如果您没有预料到这样的事情,您的设计很可能会受到影响。

      以下是您可以在运行时提取的内容的一个小示例:

      public abstract class Test<T extends Number> extends ArrayList<Long> implements Comparable<String>, List<Long>{
          public List<String> field;
          public abstract <M> M method(List<T> arg);
      
          public static void main(String... args) throws Exception {
              TypeVariable<Class<Test>> t = Test.class.getTypeParameters()[0];
              Class<?> upperBound = (Class<?>) t.getBounds()[0];
      
      
              System.out.println("Test<" + t + " extends " + upperBound.getName() + ">");
              System.out.println("extends " + Test.class.getGenericSuperclass());
              System.out.println("implements " + Arrays.toString(Test.class.getGenericInterfaces()));
              System.out.println("{");
              System.out.println(Test.class.getMethods()[1].toGenericString());
              System.out.println(Test.class.getFields()[0].toGenericString());
              System.out.println("}");
          }
      }
      

      这将输出:

      Test<T extends java.lang.Number>
      extends java.util.ArrayList<java.lang.Long>
      implements [java.lang.Comparable<java.lang.String>, java.util.List<java.lang.Long>]
      {
      public abstract <M> M Test.method(java.util.List<T>)
      public java.util.List<java.lang.String> Test.field
      }
      

      我正在使用toGenericString() 方法。然而,这只是一个漂亮的打印方法!无需解析该字符串:反射 api 提供了所有必要的信息,例如:Field.getGenericType()Method.getArgumentTypes、...

      【讨论】:

      • 让我在星期一检查一下。我们已经使用了相当多的反思,我可能需要更具体地回答我的问题,但如果没有我面前的例子,我不想说错话。
      • 好的,检查过了。这里的问题是有类似的东西: class Foo; -and- class Baz 扩展 Bar; -and- 类 Zab 扩展 Bar;当某些东西用 Baz(Bar 的子类)调用 Foo 时,我能得到的只是 Bar,而不是 Baz;我失去了子类,只能取回显式类型。这有意义吗?
      • 是的,不幸的是,这就是被删除的信息。在运行时,类 Foo (Foo.class) 只有 1 个实例。所以你只得到“声明的”泛型类型而不是“实例”泛型类型。这就是“具体化的通用”讨论(gafter.blogspot.com/2006/11/reified-generics-for-java.html)的内容。不幸的是,它也不会成为 java 7 的一部分。
      【解决方案3】:

      也许你可以使用Javassist 之类的东西来做一些元编程。

      它在 Tapestry 中用于此目的,请参阅 this 文章。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多