Avoiding Java Type Erasure

前端 未结 5 1179
既然无缘
既然无缘 2020-12-16 03:04

Is there a way one could avoid type erasure and get access to a type parameter?

public class Foo & Bar> {
    public Foo()          


        
相关标签:
5条回答
  • 2020-12-16 03:36

    AFACT, there is no practical way around type erasure because you can't ask for something which the runtime doesn't have access to. Assuming of course you agree that sub-classing generic classes for each enum which implements Bar interface is a practical work around.

    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> {
    }
    
    0 讨论(0)
  • 2020-12-16 03:39

    Use a super type token as proposed by Neil Gafter and used by libraries like guice for this purpose.

    See http://gafter.blogspot.com/2006/12/super-type-tokens.html for the original description and I've check out the guice source for CA radio life working implementation.

    there is another q which has an answer with worked example inline here How can I pass a Class as parameter and return a generic collection in Java?

    0 讨论(0)
  • 2020-12-16 03:39

    In some cases you can use a workaround suggested by Richard Gomes. When creating instances of anonymous classes, the type parameter class info is available.

    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
    }
    }
    

    Note that multiple instances created this way will be of different anonymous classes, and if that's a problem you might want a class A_String_Factory with a createNew() function based on clone to replace the calls to new.

    0 讨论(0)
  • 2020-12-16 03:48

    If you're willing/able to hand a class token to the constructor:

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

    That way, you can produce objects of type T with Class.newInstance(), attempt to cast arbitrary objects to T using Class.cast(), etc.

    What do you intend to do in baz()?

    0 讨论(0)
  • 2020-12-16 03:52

    As pholser points out in his answer, the only way to achieve this is by passing in the Class object representing the type T. It's because of Type Erasure that something like T.class isn't possible because T is erased before runtime.

    You seem resistant against passing in the Class object, but it's the only way to use the method getEnumConstants(). Here is a self contained example:

    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; }
    
    0 讨论(0)
提交回复
热议问题