unlimited101
unlimited101

Reputation: 3803

How to get (generic) type from class object

I would like to do this:

Get "Class" object from generic type T

but the other way round. So I would like to get the type of class objects. In Pseudocode I want to do something like this:

public class ExampleClass {

    public static final Class<?>[] classes = new Class[]{MyClass1.class,
    MyClass2.class, MyClass3.class, MyClass4.class, MyClass5.class};

    public void myMethod() {
        for (Class<?> c : DataBaseConfigUtils.classes ) {
            MyObjectDAO<c.getType(), Integer> myObjectDAO = getMyObjectDAO(c);
            ArrayList<c.getType()> list = myObjectDAO.queryAll();
            for (c.getType() element : list) {
                processElement(element);
            }

        }
    }

    public <T> MyObjectDAO<T, Integer> getMyObjectDAO(Class<T> c) {
        return doSomething(c);
    }
}

But there is nothing like Class.getType(). So how to get the type of a class object?

Upvotes: 3

Views: 3487

Answers (3)

dotvav
dotvav

Reputation: 2848

This is not possible in Java. The type is used only during the compilation and is erased. This information is not present in the bytecode or in the runtime environment.

https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:

  • Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
  • Insert type casts if necessary to preserve type safety.
  • Generate bridge methods to preserve polymorphism in extended generic types.

Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

Upvotes: 4

Paul Boddington
Paul Boddington

Reputation: 37645

Your generic method getMyObjectDAO seems to answer your own question. If you have a variable with a wildcard in the type, you can use a generic helper method whenever you need to refer to the type in the code. You can refactor your code into this:

public class ExampleClass {

    public static final Class<?>[] classes = new Class[]{MyClass1.class,
        MyClass2.class, MyClass3.class, MyClass4.class, MyClass5.class};

    public void myMethod() {
        for (Class<?> c : DataBaseConfigUtils.classes ) {
            act(c);    
        }
    }

    private <T> void act(Class<T> c) {
        MyObjectDAO<T, Integer> myObjectDAO = getMyObjectDAO(c);
        ArrayList<T> list = myObjectDAO.queryAll();
        for (T element : list) {
            processElement(element);
        }
    }

    public <T> MyObjectDAO<T, Integer> getMyObjectDAO(Class<T> c) {
        return doSomething(c);
    }
}

It is important to be aware of the limitations of this. Suppose you have a variable of type List<?>. While it is possible to use a private generic helper method that will enable you to treat it as a List<T> and use the type T in code, type erasure means that you can't query the type T at runtime. So all of the following are illegal

if (T == String) { //do something; }

if (T.class == String.class) { //do something }

if (a instanceof T) { //do something }

The usual workaround for this is to make one of the arguments to the helper method a Class<T> object, because you can do

if (clazz == String.class) { //do something }

(Note that this will not work if T is itself a generic type, because you cannot write List<String>.class, for example. There is a workaround called super type tokens for this.)

Since the objects you are using are Class objects, type erasure should not cause you any problems at all.

Upvotes: 1

Dmitriy
Dmitriy

Reputation: 109

For example:

ParameterizedType superclass = (ParameterizedType)getClass().getGenericSuperclass();
    this.entityClass = (Class<T>) superclass.getActualTypeArguments()[0];

But if you want to add as type Generic I suppose you have design errors. Best Practice for it - You should create Parent (abstract) class or Interface and use it in your code:

public abstract class ABaseEntity{
...
}
public class Child extends ABaseEntity{
...
}

Create base dao with your BaseEntity whiche will be return some entities extended you ABaseEntity :

public class AbstarctDAO<T extends ABaseEntity, PK extends Serializable>  {

@PersistenceContext
protected EntityManager em;
protected Class<T> entityClass;

protected AbstarctDAO() {

}
@PostConstruct
public void init(){
    final ParameterizedType superclass = (ParameterizedType)getClass().getGenericSuperclass();
    this.entityClass = (Class<T>) superclass.getActualTypeArguments()[0];

}
    @Override
    public T create(T object) throws Exception {
        em.persist(object);
        em.flush();
        return object;
    }
...
}

Excecution example: 1.

 AbstarctDAO dao = new AbstarctDAO();
        try {
            dao.create(new Child ());
        }catch (Exception e){

        }
 2.
@ Stateless
@ Transactional
public class ChildDAO extends AbstarctDAO<Child , Long> {
    @Transactional(Transactional.TxType.SUPPORTS)
    public Child getChildByName(String name) {
        ...
        return (Child)query.getSingleResult();
    }

}

Upvotes: 0

Related Questions