【问题标题】:Avoiding Java Type Erasure避免 Java 类型擦除
【发布时间】:2012-01-17 04:22:42
【问题描述】:

有没有一种方法可以避免类型擦除并访问类型参数?

public class Foo<T extends Enum<?> & Bar> {
    public Foo() {
        // access the template class here?
        // i.e. :
        baz(T.class); // obviously doesn't work
    }

    private void baz(Class<T> qux) { 
        // do stuff like
        T[] constants = qux.getEnumConstants();
        ...
    } 
}

我需要了解T,并用它来做事。是否有可能,如果可以,如何在在构造函数中或除参数之外的任何地方传入类的情况下完成?

编辑:这个问题的主要目的是找出关于类型擦除是否有任何实用的方法

【问题讨论】:

    标签: java templates generics generic-programming type-erasure


    【解决方案1】:

    事实,没有类型擦除的实用方法,因为你不能要求运行时无权访问的东西。当然,假设您同意为每个实现 Bar 接口的枚举子类化泛型类是一项实际工作。

    enum Test implements Bar {
        ONE, TWO
    }
    
    class Foo<T> extends FooAbstract<Test> {
        public Foo() {
            ParameterizedType genericSuperclass =
                    (ParameterizedType) getClass().getGenericSuperclass();
            baz((Class<T>) genericSuperclass.getActualTypeArguments()[0]);
        }
    
        private void baz(Class<T> qux) {
            T[] constants = qux.getEnumConstants();
            System.out.println(Arrays.toString(constants)); // print [ONE, TWO]
        }
    }
    
    interface Bar {
    }
    
    class FooAbstract<T extends Enum<?> & Bar> {
    }
    

    【讨论】:

    • +1 是的 - 如果您更喜欢子类化而不是传递 Class 对象,这是另一种基于反射的替代方案。
    【解决方案2】:

    如果您愿意/能够将类令牌交给构造函数:

    public Foo(Class<T> clazz) {
        baz(clazz);
    }
    
    private void baz(Class<T> qux) {
        // ...
    }
    

    这样,您可以使用 Class.newInstance() 生成 T 类型的对象,尝试使用 Class.cast() 将任意对象强制转换为 T,等等。

    你打算在 baz() 中做什么?

    【讨论】:

    • 假设它向给定类询问值列表并使用这些值。及格不是我想做的。
    【解决方案3】:

    正如 pholser 在他的回答中指出的那样,实现这一点的唯一方法是传入代表 T 类型的 Class 对象。正是因为Type ErasureT.class 之类的东西是不可能的,因为T 在运行前被删除了。

    您似乎反对传入Class 对象,但这是使用方法getEnumConstants() 的唯一方法。这是一个自包含的示例:

    public class Foo<T extends Enum<?> & Bar> {
    
       final Class<T> clazz;
    
       public Foo(Class<T> clazz) {
    
          this.clazz = clazz;
       }
    
       public void baz() {
    
          T[] constants = clazz.getEnumConstants();
    
          System.out.println(Arrays.toString(constants));
       }
    
       public static void main(String[] args) {
    
           new Foo<MyEnum>(MyEnum.class).baz(); //prints "[A, B, C, D]"
       }
    }
    
    public interface Bar { }
    
    public enum MyEnum implements Bar { A, B, C, D; }
    

    【讨论】:

    • 所以我绝对没有办法绕过类型擦除并找出作为T 传入的类?这个线程的想法是找出是否有任何方法可以解决类型擦除。
    • @Chris - 反射是 Java 中类型擦除的唯一解决方法。但是通过传入Class 对象,您不会再处于劣势,尽管它很烦人并且在编译时看起来是多余的。请注意,您已经使用 Class#getEnumConstants() 依赖反射。唯一的选择是彻底重新设计,消除您对该方法的依赖。
    【解决方案4】:

    使用 Neil Gafter 提出的超类型标记,并被 guice 等库用于此目的。

    有关原始描述,请参阅 http://gafter.blogspot.com/2006/12/super-type-tokens.html,我查看了 CA radio life working implementation 的 guice 源。

    还有另一个 q 有一个答案,这里有工作示例How can I pass a Class as parameter and return a generic collection in Java?

    【讨论】:

      【解决方案5】:

      在某些情况下,您可以使用 Richard Gomes 建议的解决方法。 创建匿名类的实例时,类型参数类信息可用。

      class A<T>
      {
          A()
          {
              java.lang.reflect.ParameterizedType parameterizedType = (java.lang.reflect.ParameterizedType)  (this.getClass().getGenericSuperclass());
              System.out.println(parameterizedType.getActualTypeArguments()[0]);
          }
      }
      
      public class Example {
       public static void main(String[] args) {
           A<String> anonymous = new A<String>() {};  // prints java.lang.String
      }
      }
      

      请注意,以这种方式创建的多个实例将属于不同的匿名类,如果这是一个问题,您可能需要一个具有基于克隆的 createNew() 函数的 A_String_Factory 类来替换对 new 的调用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-06-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多