Pete
Pete

Reputation: 10938

Get the generic type of an extending generic class in the constructor of the superclass in Java?

I have an abstract class Abstr<T> and an extending class Ext<T>. I need Abstr to know, with which T its extension was initialized so it can return that type. So far I've done this by giving the type to the constructor and then calling the superconstructor to save it. But this should work with reflections too I think. So far like I said:

public abstract class Abstr<T> {

    private Class<T> type;

    public Abstr(Class<T> _class) {
        this.type = _class;
    }

}

public class Ext<T> extends Abstr<T> {

    public Ext(Class<T> _class) {
        super(_class);
    }

}

public void someMethod() {
    Ext<String> ext = new Ext<String>(String.class);
}

Using reflections should rid me of calling any constructor as the extending default constructor will call the abstract classes default constructor which should then save the type of the calling constructor:

public Abstr() {    
    this.type = (Class<D>) this.getClass().getTheTypeParameter(); // something along these lines
}

Thanks!

Upvotes: 0

Views: 1320

Answers (4)

Thomas
Thomas

Reputation: 88757

This might not always work, since if you just have new Ext<String>() in some method there's no reflection data for the type.

Thus Ext<String> ext = new Ext<String>(String.class); would loose the type information at runtime (type erasure) effectively resulting in Ext ext = new Ext(String.class);.

If you have fields like private Ext<String> stringExt or concrete subclasses like StringExt extends Ext<String> you have reflection data you could retrieve.

Here's a short article with some source that you could use to get reflection data for concrete subclasses (and maybe for fields, but I didn't test that): http://www.artima.com/weblogs/viewpost.jsp?thread=208860

As I said, we're using an adjusted version of that code to get type parameters for concrete subclasses.

So we have something like this:

class AbstractDAO<T entityType> {
  protected Class<?> entityClass;

  protected AbstractDAO() {
    //ReflectionUtil is our adjusted version of code in the link above
    entityClass = ReflectionUtil.getTypeArguments(AbstractDAO.class, getClass() ).get(0);
  }
}

//Here entityClass is User.class
class UserDAO extends AbstractDAO<User> { ... }

Upvotes: 1

Peter Lawrey
Peter Lawrey

Reputation: 533880

The generic type of Abstr is T This all you can get via reflection.

If you want to simplify your code you can write.

public class Ext<T> extends Abstr<T> {
    protected Ext(Class<T> _class) {
        super(_class);
    }

    public static <T> Ext<T> of(Class<T> _class) { return new Exit<T>(_class); }
}

Ext<String> ext = Ext.of(String.class);

Upvotes: 1

alf
alf

Reputation: 8523

The only case when it works is when you have a non-generic class with a generic parent. An example would be,

class Trick<T> {
    T t;
}

public class GenericTest {
    public static void main(String[] args) {
        Trick<Set<String>> trick = new Trick<Set<String>>() {
        };

        // Prints "class org.acm.afilippov.GenericTest$1"
        System.out.println(trick.getClass());
        // Prints "org.acm.afilippov.Trick<java.util.Set<java.lang.String>>"
        System.out.println(trick.getClass().getGenericSuperclass());
    }
}

For further reading refer to the Reflecting generics tutorial — the answer is there, but it's way too long to quote it and not feel as if I was stealing a code. So please read there :)

Upvotes: 3

Robin
Robin

Reputation: 36621

If you really need a method in Abstr similar to

public Class<T> getType()

you have no choice but to pass the Class isntance in the constructor (or in any other method). You cannot retrieve the Class of T due to type erasure, which basically comes down to the fact that T is not available at runtime, only at compile time.

Upvotes: 1

Related Questions