9

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

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();
        ...
    } 
}

I need to know about T, and do things with it. Is it possible, and if so, how can it be done without passing in the class in the constructor or anywhere besides the parameter?

EDIT: The main purpose of this question is to find out if there is any practical way around type erasure.

Chris Cashwell
  • 22,308
  • 13
  • 63
  • 94

5 Answers5

6

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> {
}
Sanjay T. Sharma
  • 22,857
  • 4
  • 59
  • 71
2

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

pholser
  • 4,908
  • 1
  • 27
  • 37
  • Let's say it asks the given class for a list of values and makes use of those values. Passing in the class is not what I want to do. – Chris Cashwell Dec 09 '11 at 23:29
1

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; }
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
  • 2
    So there's absolutely no way for me to get around the Type Erasure and figure out the class passed in as `T`? The idea of this thread is to find out if there is any way around type erasure. – Chris Cashwell Dec 10 '11 at 20:00
  • 1
    @Chris - Reflection is the only workaround to type erasure in Java. But you're put at no more disadvantage from what you have by passing in the `Class` object, though it's annoying and looks redundant at compile time. Notice that you're already relying on reflection by using `Class#getEnumConstants()`. The only alternative is a complete redesign that removes your dependence on that method. – Paul Bellora Dec 10 '11 at 20:57
1

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?

Community
  • 1
  • 1
Matt
  • 8,367
  • 4
  • 31
  • 61
0

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.

Gonen I
  • 5,576
  • 1
  • 29
  • 60