Home > Software engineering >  java generics reflection: get Class<T> from generic returns TypeVariableImpl instead of
java generics reflection: get Class<T> from generic returns TypeVariableImpl instead of

Time:10-07

This is my code, shared here, so you can run it.

public interface GenericRepository<T> {
    default Class<T> getEntityClazz() {
        ParameterizedType pType = (ParameterizedType)this.getClass().getGenericInterfaces()[0];
        Type tT = pType.getActualTypeArguments()[0];
        Class<T> clazz = (Class<T>) tT.getClass();
        
        return clazz;
    }
}
public class GenericRepositoryImpl<T> implements GenericRepository<T> {
    
}

My main is:

public class Main
{
    public static void main(String[] args) {
        GenericRepository<Example> genericObject = new GenericRepositoryImpl();
        Class<Example> clazz = genericObject.getEntityClazz();
        System.out.println("Generic class: "   clazz.toString());
    }
}

And output is:

Generic class: class sun.reflect.generics.reflectiveObjects.TypeVariableImpl

I was expecting to get Generic class: class Example.

Any ideas?

CodePudding user response:

In Java you deal with generic type erasure, so...

public interface GenericRepository<T> {
    Class<T> getEntityClazz();
}

//declared as abstract so override of getEntityClazz() is not required here
public abstract class GenericRepositoryImpl<T> implements GenericRepository<T> {
    //common fields and logic
}

public class ExampleReposirory extends GenericRepositoryImpl<Example> {
    //constructor calling super
    @Override
    public Class<T> getEntityClazz() {
        return Example.class;
    }
}

This approach is rather verbose, but at least wins in clarity and straightforwardness compared to the magic of reflection

CodePudding user response:

When you use this.getClass(), you're basically dealing with the definition of the class, not the INSTANCE which holds the generic information you may need. If you declaring you generic type like A<T extends B>, then you can retrieve the type of B by using ((TypeVariableImpl)pType.getActualTypeArguments()[0]).getBounds()[0], but I don't think that's what you want to do.

So I would suggest 2 other alternatives:

1, Just return the class type in your implementation class, in with the generic type is explicitly specified:

public class GenericRepositoryImpl implements GenericRepository<Example> {
@Override
public Class<Example> getEntityClazz() {
    return Example.class;
    }
}

2, When you still want to get the type of generic type in the generic class itself, then save it somewhere when you initialize the object, the trade-off is that you have to put that type in as an argument to initialize:

public class GenericRepositoryImpl<T> implements GenericRepository<T> {
private final Class<T> type;

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

@Override
public Class<T> getEntityClazz() {
    return this.type;
    }
}

And the usage would be like this :

public class Main {
public static void main(String[] args) {
    GenericRepository<Example> genericObject = new GenericRepositoryImpl(Example.class);
    System.out.println("Generic class: "   genericObject.getEntityClazz().toString());
    }
}
  • Related