用反射获取Java中的泛型参数的类型

是否有可能获得一个通用参数的类型?

一个例子:

public final class Voodoo { public static void chill(List<?> aListWithTypeSpiderMan) { // Here I'd like to get the Class-Object 'SpiderMan' Class typeOfTheList = ???; } public static void main(String... args) { chill(new ArrayList<SpiderMan>()); } } 

    我曾经偶然发现的一个结构看起来像

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

    所以似乎有一些反思,周围的魔法,我不好意思不完全明白…对不起。

    我想尝试打破@DerMike的解答:

    首先,类型擦除并不意味着JDK在运行时消除类型信息。 这是一种允许编译时类型检查和运行时类型兼容性以相同语言共存的方法。 正如这段代码所暗示的那样,JDK保留了已擦除的类型信息 – 它与检查的类型和内容没有关联。

    其次,它将通用类型信息提供给通用类,与被检查的具体类型相比,只有一层,即具有通用类型参数的抽象父类可以找到与其类型参数相对应的具体类型,以用于自身的具体实现直接从它继承。 如果这个类是非抽象的和实例化的,或者具体的实现是两个层次的,那么这是行不通的(尽管有一点点模糊可以使它适用于任何预定数量的层次,超过一层,或者最低层X通用类型参数等)。

    无论如何,在解释。 这里的代码再次分开,以便于参考:

     1#类genericParameter0OfThisClass = 
     2#(班)
     3#((参数化类型)
     4#getClass()
     5#.getGenericSuperclass())
     6#.getActualTypeArguments()[0];
    

    让“我们”成为包含此代码的泛型类型的抽象类。 仔细阅读这个内容:

    • 第4行获取当前具体类的Class实例。 这标识了我们的直系后代的具体类型。
    • 第5行将类“超类型”作为类型; 这是我们。 由于我们是参数类型,我们可以安全地将自己投射到ParameterizedType(第3行)。 关键在于,当Java确定此Type对象时,它使用子对象中的类型信息将类型信息与新的ParameterizedType实例中的类型参数相关联。 所以现在我们可以访问我们的泛型的具体类型。
    • 第6行按照类代码中声明的顺序获取映射到我们泛型的类型数组。 对于这个例子,我们拉出第一个参数。 这回来作为一个类型。
    • 第2行将返回到类的最终类型。 这是安全的,因为我们知道我们的泛型类型参数能够采用哪些类型,并且可以确认它们都是类(我不确定在Java中如何获取没有Class实例的泛型参数与之相关,实际上)。

    …这就是它。 所以我们把类型信息从我们自己的具体实现中推回到自己,并用它来访问一个类的句柄。 我们可以将getGenericSuperclass()加倍,然后转到两个级别,或者消除getGenericSuperclass(),并将自己的值作为一个具体的类型(注意:我还没有测试过这些场景,他们还没有拿出来)。

    如果你的具体的孩子是一个任意数量的啤酒花,或者如果你是具体的而不是最终的,特别棘手的是,如果你期望你的任何(不同的深度)的孩子有自己的仿制药。 但是你通常可以围绕这些考虑进行设计,所以这就使你获得了最大的成就。

    希望这有助于某人! 我承认这个帖子很古老。 我可能会剪下这个解释,并保留其他的问题。

    其实我得到了这个工作。 考虑下面的代码片段:

     Method m; Type[] genericParameterTypes = m.getGenericParameterTypes(); for (int i = 0; i < genericParameterTypes.length; i++) { if( genericParameterTypes[i] instanceof ParameterizedType ) { Type[] parameters = ((ParameterizedType)genericParameterTypes[i]).getActualTypeArguments(); //parameters[0] contains java.lang.String for method like "method(List<String> value)" } } 

    我正在使用JDK 1.6

    实际上有一个解决方案,通过应用“匿名类”技巧和来自超类型令牌的想法:

     public final class Voodoo { public static void chill(final List<?> aListWithSomeType) { // Here I'd like to get the Class-Object 'SpiderMan' System.out.println(aListWithSomeType.getClass().getGenericSuperclass()); System.out.println(((ParameterizedType) aListWithSomeType .getClass() .getGenericSuperclass()).getActualTypeArguments()[0]); } public static void main(String... args) { chill(new ArrayList<SpiderMan>() {}); } } class SpiderMan { } 

    诀窍在于创建一个匿名类new ArrayList<SpiderMan>() {} ,取代原来的(简单的) new ArrayList<SpiderMan>() 。 使用类(如果可能的话)确保编译器保留有关类型参数SpiderMan给类型参数List<?> 。 Voilà!

    不,这是不可能的。 由于向下兼容性问题,Java的泛型基于类型擦除 ,在运行时,您拥有的只是一个非泛型的List对象。 在运行时有一些关于类型参数的信息,但它驻留在类定义中(即,你可以问“ 这个字段的定义使用了什么样的通用类型 ”),而不是在对象实例中。

    正如@bertolami所指出的那样,我们不可能使用变量类型并获得它的未来值(typeOfList变量的内容)。

    不过,你可以像这样传递类作为参数:

     public final class voodoo { public static void chill(List<T> aListWithTypeSpiderMan, Class<T> clazz) { // Here I'd like to get the Class-Object 'SpiderMan' Class typeOfTheList = clazz; } public static void main(String... args) { chill(new List<SpiderMan>(), Spiderman.class ); } } 

    当你不得不把一个类变量传递给ActivityInstrumentationTestCase2的构造函数时,这或多或少是Google做的。

    由于类型擦除,知道列表类型的唯一方法是将类型作为参数传递给方法:

     public class Main { public static void main(String[] args) { doStuff(new LinkedList<String>(), String.class); } public static <E> void doStuff(List<E> list, Class<E> clazz) { } } 

    附录@ DerMike获取参数化接口的泛型参数的答案在Java-8默认方法中使用#getGenericInterfaces()方法以避免重复):

     import java.lang.reflect.ParameterizedType; public class ParametrizedStuff { @SuppressWarnings("unchecked") interface Awesomable<T> { default Class<T> parameterizedType() { return (Class<T>) ((ParameterizedType) this.getClass().getGenericInterfaces()[0]) .getActualTypeArguments()[0]; } } static class Beer {}; static class EstrellaGalicia implements Awesomable<Beer> {}; public static void main(String[] args) { System.out.println("Type is: " + new EstrellaGalicia().parameterizedType()); // --> Type is: ParameterizedStuff$Beer } 

    不,这是不可能的。

    你可以得到一个领域的泛型类型给予一个类是唯一的例外,甚至这是一个黑客。

    有关这方面的示例,请参阅了解Java中的泛型类型 。

    这是不可能的,因为Java中的泛型只是在编译时考虑的。 因此,Java泛型只是某种预处理器。 但是,您可以获得列表中成员的实际类别。

    使用:

     Class<?> typeOfTheList = aListWithTypeSpiderMan.toArray().getClass().getComponentType(); 

    你可以像我在这里找到的例子那样获得一个泛型参数的类型:

     import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; public class Home<E> { @SuppressWarnings ("unchecked") public Class<E> getTypeParameterClass(){ Type type = getClass().getGenericSuperclass(); ParameterizedType paramType = (ParameterizedType) type; return (Class<E>) paramType.getActualTypeArguments()[0]; } private static class StringHome extends Home<String>{} private static class StringBuilderHome extends Home<StringBuilder>{} private static class StringBufferHome extends Home<StringBuffer>{} /** * This prints "String", "StringBuilder" and "StringBuffer" */ public static void main(String[] args) throws InstantiationException, IllegalAccessException { Object object0 = new StringHome().getTypeParameterClass().newInstance(); Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance(); Object object2 = new StringBufferHome().getTypeParameterClass().newInstance(); System.out.println(object0.getClass().getSimpleName()); System.out.println(object1.getClass().getSimpleName()); System.out.println(object2.getClass().getSimpleName()); } } 

    我已经编写了这个方法,希望接受或返回Iterable<?...> 。 这里是代码:

     /** * Assuming the given method returns or takes an Iterable<T>, this determines the type T. * T may or may not extend WindupVertexFrame. */ private static Class typeOfIterable(Method method, boolean setter) { Type type; if (setter) { Type[] types = method.getGenericParameterTypes(); // The first parameter to the method expected to be Iterable<...> . if (types.length == 0) throw new IllegalArgumentException("Given method has 0 params: " + method); type = types[0]; } else { type = method.getGenericReturnType(); } // Now get the parametrized type of the generic. if (!(type instanceof ParameterizedType)) throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method); ParameterizedType pType = (ParameterizedType) type; final Type[] actualArgs = pType.getActualTypeArguments(); if (actualArgs.length == 0) throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method); Type t = actualArgs[0]; if (t instanceof Class) return (Class<?>) t; if (t instanceof TypeVariable){ TypeVariable tv = (TypeVariable) actualArgs[0]; AnnotatedType[] annotatedBounds = tv.getAnnotatedBounds();/// GenericDeclaration genericDeclaration = tv.getGenericDeclaration();/// return (Class) tv.getAnnotatedBounds()[0].getType(); } throw new IllegalArgumentException("Unknown kind of type: " + t.getTypeName()); } 

    您无法从变量中获取通用参数。 但你可以从一个方法或字段声明:

     Method method = getClass().getDeclaredMethod("chill", List.class); Type[] params = method.getGenericParameterTypes(); ParameterizedType firstParam = (ParameterizedType) params[0]; Type[] paramsOfFirstGeneric = firstParam.getActualTypeArguments(); 

    只是为了阅读这段代码很难,我只是把它分成两行:

     // assuming that the Generic Type parameter is of type "T" ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass(); Class<T> c =(Class<T>)p.getActualTypeArguments()[0]; 

    我想创建一个Type参数的实例,而没有任何参数给我的方法:

     publc T getNewTypeInstance(){ ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass(); Class<T> c =(Class<T>)p.getActualTypeArguments()[0]; // for me i wanted to get the type to create an instance // from the no-args default constructor T t = null; try{ t = c.newInstance(); }catch(Exception e){ // no default constructor available } return t; }