61

I'm creating a generic class and in one of the methods I need to know the Class of the generic type currently in use. The reason is that one of the method's I call expects this as an argument.

Example:

public class MyGenericClass<T> {
  public void doSomething() {
    // Snip...
    // Call to a 3rd party lib
    T bean = (T)someObject.create(T.class);
    // Snip...
  }
}

Clearly the example above doesn't work and results in the following error: Illegal class literal for the type parameter T.

My question is: does someone know a good alternative or workaround for this?

Jaap Coomans
  • 1,465
  • 4
  • 15
  • 24

6 Answers6

49

Still the same problems : Generic informations are erased at runtime, it cannot be recovered. A workaround is to pass the class T in parameter of a static method :

public class MyGenericClass<T> {

    private final Class<T> clazz;

    public static <U> MyGenericClass<U> createMyGeneric(Class<U> clazz) {
        return new MyGenericClass<U>(clazz);
    }

    protected MyGenericClass(Class<T> clazz) {
        this.clazz = clazz;
    }

    public void doSomething() {
        T instance = clazz.newInstance();
    }
}

It's ugly, but it works.

Nicolas
  • 24,509
  • 5
  • 60
  • 66
  • 1
    It sure is ugly, but the trick with protected constructor is good enough for me. I'm using the generic class as an abstract class that serves as the base 5 to 10 concrete classes. Thanks! – Jaap Coomans Oct 08 '08 at 13:36
  • Is it possible to use this to determine what generic type an object is? eg `if (obj.getClazz() is a String) doThis(); if (obj.getClazz() is a Integer) doThat(); ` – dwjohnston Nov 09 '12 at 00:09
  • 1
    ^Solution: `if(obj.getClazz().equals(String.class))... ` – dwjohnston Nov 09 '12 at 01:14
  • @dwjohnston That assumes you have an instance of the class already. – Basic Dec 08 '14 at 12:34
  • +1 I just call my factory method `of`. Compare `MyGenericClass.of(String.class)` with `new MyGenericClass() {}`. The reflective approach doesn't save anything. – Radiodef May 16 '15 at 00:41
  • @dwjohnston uh why not just `if(obj instanceof String)...`? – ADTC Apr 23 '18 at 04:33
  • @Nicolas I don't get the point of static method, I'd just make the constructor `public`. Also, following the asker's code, it would be `T bean = (T)someObject.create(clazz);` – ADTC Apr 23 '18 at 04:44
23

I was just pointed to this solution:

import java.lang.reflect.ParameterizedType;

public abstract class A<B> {
    public Class<B> g() throws Exception {
        ParameterizedType superclass =
            (ParameterizedType) getClass().getGenericSuperclass();

        return (Class<B>) superclass.getActualTypeArguments()[0];
    }
}

This works if A is given a concrete type by a subclass:

new A<String>() {}.g() // this will work

class B extends A<String> {}
new B().g() // this will work

class C<T> extends A<T> {}
new C<String>().g() // this will NOT work
Radiodef
  • 37,180
  • 14
  • 90
  • 125
Christoph
  • 231
  • 2
  • 2
4

Unfortunately Christoph's solution as written only works in very limited circumstances. [EDIT: as commented below I no longer remember my reasoning for this sentence and it is likely wrong: "Note that this will only work in abstract classes, first of all."] The next difficulty is that g() only works from DIRECT subclasses of A. We can fix that, though:

private Class<?> extractClassFromType(Type t) throws ClassCastException {
    if (t instanceof Class<?>) {
        return (Class<?>)t;
    }
    return (Class<?>)((ParameterizedType)t).getRawType();
}

public Class<B> g() throws ClassCastException {
    Class<?> superClass = getClass(); // initial value
    Type superType;
    do {
        superType = superClass.getGenericSuperclass();
        superClass = extractClassFromType(superType);
    } while (! (superClass.equals(A.class)));

    Type actualArg = ((ParameterizedType)superType).getActualTypeArguments()[0];
    return (Class<B>)extractClassFromType(actualArg);
}

This will work in many situations in practice, but not ALL the time. Consider:

public class Foo<U,T extends Collection<?>> extends A<T> {}

(new Foo<String,List<Object>>() {}).g();

This will throw a ClassCastException, because the type argument here isn't a Class or a ParameterizedType at all; it's the TypeVariable T. So now you would be stuck trying to figure out what type T was supposed to stand for, and so on down the rabbit hole.

I think the only reasonable, general answer is something akin to Nicolas's initial answer -- in general, if your class needs to instantiate objects of some other class that is unknown at compile-time, users of your class need to pass that class literal (or, perhaps, a Factory) to your class explicitly and not rely solely on generics.

  • Christoph's solution does work with non-abstract classes, I've tested it and included my work in an answer here. – intrepidis Aug 11 '14 at 21:33
  • @ChrisNash You are probably right about that -- it's been close to 4 years since I wrote this, I don't write much Java anymore, and it's not immediately clear to me what I meant by the "abstract classes" comment either. I still stand by the general correctness of Nicolas's and my answers. I suppose it might be _possible_ to write a method that went all the way "down the rabbit hole", as I said, but I'm not sure I'd want to.... – Steven Collins Dec 14 '14 at 21:32
2

i find another way to obtain the Class of the generic object

public Class<?> getGenericClass(){
         Class<?> result =null;
         Type type =this.getClass().getGenericSuperclass();

         if(type instanceofParameterizedType){
              ParameterizedType pt =(ParameterizedType) type;
              Type[] fieldArgTypes = pt.getActualTypeArguments();
              result =(Class<?>) fieldArgTypes[0];
        }
        return result;
  }
Jet Geng
  • 347
  • 1
  • 7
  • 1
    Well now i know why this happens, this only works for abstract classes that require the definition of implementation, and even than you are required to define the T type in the implementation and cannot allow the setting of this value during the use of the new class. it wont work in certain cases – Nuno Furtado Nov 09 '11 at 10:54
0

I will elaborate on Christoph's solution.

Here is the ClassGetter abstract class:

private abstract class ClassGetter<T> {
    public final Class<T> get() {
        final ParameterizedType superclass = (ParameterizedType)
            getClass().getGenericSuperclass();
        return (Class<T>)superclass.getActualTypeArguments()[0];
    }
}

Here is a static method which uses the above class to find a generic class' type:

public static <T> Class<T> getGenericClass() {
    return new ClassGetter<T>() {}.get();
}

As an example of it's usage, you could make this method:

public static final <T> T instantiate() {
    final Class<T> clazz = getGenericClass();
    try {
        return clazz.getConstructor((Class[])null).newInstance(null);
    } catch (Exception e) {
        return null;
    }
}

And then use it like this:

T var = instantiate();
intrepidis
  • 2,870
  • 1
  • 34
  • 36
  • This code doesn't work. It throws `java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class`. – Radiodef May 15 '15 at 21:07
  • What is the type of the generic `T` that you are using? – intrepidis May 18 '15 at 08:44
-1

public class DatabaseAccessUtil {

EntityManagerFactory entitymanagerfactory;
EntityManager entitymanager;

public DatabaseAccessUtil() {
    entitymanagerfactory=Persistence.createEntityManagerFactory("bookmyshow");
    entitymanager=entitymanagerfactory.createEntityManager();
}

public void save  (T t) {
    entitymanager.getTransaction().begin();
    entitymanager.persist(t);
    entitymanager.getTransaction().commit();
}

public void update(T t) {
    entitymanager.getTransaction().begin();
    entitymanager.persist(t);
    entitymanager.getTransaction().commit();
}

public void delete(T t) {
    entitymanager.getTransaction().begin();
    entitymanager.remove(t);
    entitymanager.getTransaction().commit();
}

public Object retrieve(Query query) {
    return query.getSingleResult();
}
//call the method - retrieve(object,requiredclass.class)
public Object retrieve(Object primaryKey,class clazz) throws Exception {

    return entitymanager.find(clazz,primaryKey);    

}

}

  • Please format your code block properly and include `public class DatabaseAccessUtil {` and ending `}` as well to code. Also, it would be better if you add some verbiage rather then just code. – Red Boy May 29 '18 at 06:00
  • This has very little or no relation to the question. It is not a solution to the problem and it involves JPA, which does not even occur in the question. – Jaap Coomans Jun 04 '18 at 11:29