How to get a class instance of generics type T

2018-12-31 02:49发布

I have a generics class, Foo<T>. In a method of Foo, I want to get the class instance of type T, but I just can't call T.class.

What is the preferred way to get around it using T.class?

标签: java generics
17条回答
伤终究还是伤i
2楼-- · 2018-12-31 03:24

Here is a working solution:

@SuppressWarnings("unchecked")
private Class<T> getGenericTypeClass() {
    try {
        String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
        Class<?> clazz = Class.forName(className);
        return (Class<T>) clazz;
    } catch (Exception e) {
        throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
    }
} 

NOTES: Can be used only as superclass

  1. Has to be extended with typed class (Child extends Generic<Integer>)

OR

  1. Has to be created as anonymous implementation (new Generic<Integer>() {};)
查看更多
牵手、夕阳
3楼-- · 2018-12-31 03:26

There is a small loophole however: if you define your Foo class as abstract. That would mean you have to instantiate you class as:

Foo<MyType> myFoo = new Foo<MyType>(){};

(Note the double braces at the end.)

Now you can retrieve the type of T at runtime:

Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];

Note however that mySuperclass has to be the superclass of the class definition actually defining the final type for T.

It is also not very elegant, but you have to decide whether you prefer new Foo<MyType>(){} or new Foo<MyType>(MyType.class); in your code.


For example:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.NoSuchElementException;

/**
 * Captures and silently ignores stack exceptions upon popping.
 */
public abstract class SilentStack<E> extends ArrayDeque<E> {
  public E pop() {
    try {
      return super.pop();
    }
    catch( NoSuchElementException nsee ) {
      return create();
    }
  }

  public E create() {
    try {
      Type sooper = getClass().getGenericSuperclass();
      Type t = ((ParameterizedType)sooper).getActualTypeArguments()[ 0 ];

      return (E)(Class.forName( t.toString() ).newInstance());
    }
    catch( Exception e ) {
      return null;
    }
  }
}

Then:

public class Main {
    // Note the braces...
    private Deque<String> stack = new SilentStack<String>(){};

    public static void main( String args[] ) {
      // Returns a new instance of String.
      String s = stack.pop();
      System.out.printf( "s = '%s'\n", s );
    }
}
查看更多
美炸的是我
4楼-- · 2018-12-31 03:26

Imagine you have an abstract superclass that is generic:

public abstract class Foo<? extends T> {}

And then you have a second class that extends Foo with a generic Bar that extends T:

public class Second extends Foo<Bar> {}

You can get the class Bar.class in the Foo class by selecting the Type (from bert bruynooghe answer) and infering it using Class instance:

Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
//Parse it as String
String className = tType.toString().split(" ")[1];
Class clazz = Class.forName(className);

You have to note this operation is not ideal, so it is a good idea to cache the computed value to avoid multiple calculations on this. One of the typical uses is in generic DAO implementation.

The final implementation:

public abstract class Foo<T> {

    private Class<T> inferedClass;

    public Class<T> getGenericClass(){
        if(inferedClass == null){
            Type mySuperclass = getClass().getGenericSuperclass();
            Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
            String className = tType.toString().split(" ")[1];
            inferedClass = Class.forName(className);
        }
        return inferedClass;
    }
}

The value returned is Bar.class when invoked from Foo class in other function or from Bar class.

查看更多
余欢
5楼-- · 2018-12-31 03:29

I have an (ugly but effective) solution for this problem, which I used recently:

import java.lang.reflect.TypeVariable;


public static <T> Class<T> getGenericClass()
{
    __<T> ins = new __<T>();
    TypeVariable<?>[] cls = ins.getClass().getTypeParameters(); 

    return (Class<T>)cls[0].getClass();
}

private final class __<T> // generic helper class which does only provide type information
{
    private __()
    {
    }
}
查看更多
心情的温度
6楼-- · 2018-12-31 03:29
   public <T> T yourMethodSignature(Class<T> type) {

        // get some object and check the type match the given type
        Object result = ...            

        if (type.isAssignableFrom(result.getClass())) {
            return (T)result;
        } else {
            // handle the error
        }
   }
查看更多
登录 后发表回答