Avoiding Java Type Erasure

∥☆過路亽.° 提交于 2019-11-29 04:09:09

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

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()?

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

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?

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!