在运行时获取泛型类型:在某些情况下工作,但在其他情况下不工作 - 为什么?

时间:2015-09-22 12:24:39

标签: java generics dynamic

我正在尝试获取类或接口实现的泛型类型。我知道这有一些风险和怪癖,但我想了解什么是可能的。

这是我的示例代码:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.LinkedList;

public class Main {

    public interface MyInterface<T> {
    }

    public static class BaseImpl<T> implements MyInterface<T> {
    }

    public static class TypedImpl extends BaseImpl<Integer> {
    }

    public static void main(String[] args) throws Exception {
        LinkedList<MyInterface<Integer>> instances = new LinkedList<>();

        // 1. anonymous class from interface
        instances.add(new MyInterface<Integer>() {
        });

        // 2. class extending interface
        instances.add(new BaseImpl<Integer>());

        // 3. class with pre-defined generic type
        instances.add(new TypedImpl());

        for (MyInterface<Integer> instance : instances) {
            System.out.println("----------------------------------------------");

            Class clazz = instance.getClass();
            Type genericSuper = clazz.getGenericSuperclass();
            Type[] genericInterfaces = clazz.getGenericInterfaces();
            Class target = null;

            System.out.println("class: " + clazz.getName());
            System.out.println("generic super: " + genericSuper);
            System.out.println("generic interfaces: " + Arrays.asList(genericInterfaces));

            // attempt to 'extract' generic type
            if (genericSuper instanceof ParameterizedType) {
                target = getGeneric((ParameterizedType) genericSuper);
            } else if (genericInterfaces.length > 0) {
                for (Type genericInterface : genericInterfaces) {
                    if (genericInterface instanceof ParameterizedType) {
                        target = getGeneric((ParameterizedType) genericInterface);

                        if (null != target) {
                            break;
                        }
                    }
                }
            }

            System.out.println("TARGET: " + target);
        }
    }

    // attempt to get type argument
    public static Class getGeneric(ParameterizedType type) {
        if (MyInterface.class.isAssignableFrom((Class) type.getRawType())) {
            Type typeArg = type.getActualTypeArguments()[0];

            try {
                return (Class) typeArg;
            } catch (ClassCastException e) {
                System.out.println("cast exception for '" + typeArg + "'");
            }
        }

        return null;
    }

}

这个输出是:

----------------------------------------------
class: Main$1
generic super: class java.lang.Object
generic interfaces: [Main.Main$MyInterface<java.lang.Integer>]
TARGET: class java.lang.Integer
----------------------------------------------
class: Main$BaseImpl
generic super: class java.lang.Object
generic interfaces: [Main.Main$MyInterface<T>]
cast exception for 'T'
TARGET: null
----------------------------------------------
class: Main$TypedImpl
generic super: Main.Main$BaseImpl<java.lang.Integer>
generic interfaces: []
TARGET: class java.lang.Integer

因此,我的目标是让target变量的值为Integer.class。对于匿名类(#1)和显式类型的实现(#3),我能够按预期找到目标。为什么它在这些实例中工作而不是在#2(BaseImpl实例)的情况下?还有另一种方法来实现这一目标吗? (我知道通过实现类的构造函数传递目标Class的常见解决方法,但我有兴趣动态地执行此操作)

我咨询了以下来源:

提前多多感谢,

阿贝尔

2 个答案:

答案 0 :(得分:1)

来自http://tutorials.jenkov.com/java-reflection/generics.html(Bolds是我的)

  

使用Java Generics通常属于两种不同之一   情况

     

将类/接口声明为可参数化。用一个   可参数化的类。当你编写类或接口时,你可以   指定它应该是可参数化的。就是这种情况   java.util.List接口。而不是创建一个Object列表   参数化java.util.List以创建一个表示String的列表。

     

运行时检查可参数化类型本身,如   java.util.List,无法知道已经是什么类型   参数化为。这是有道理的,因为类型可以参数化   在同一个应用程序中的各种类型。 但是,当你检查   您声明使用参数化类型的方法或字段   可以在运行时看到参数化参数化类型的类型   至即可。简而言之:

     

您无法在类型本身上看到它被参数化为a的类型   运行时,但您可以在使用它的字段和方法中看到它   参数即可。换句话说,它的具体参数化。

特定情况在页面上解释

答案 1 :(得分:0)

在案例1和案例3中,类型是类Main$1Main$TypedImpl的反射数据的一部分,因此您可以访问它。

在案例2中,您只有实例数据,由于类型擦除不包含任何类型信息。因此,您无法在此处访问通用类型。

进一步解释:

在运行时,系统无法在BaseImpl<Integer> i;BaseImpl<String> s;之间进行分析,即由于类型删除变量is属于BaseImpl类型。

相比之下,TypedImpl t;已知类型为TypedImpl并使用反射,您可以提取extendsimplements语句中提供的泛型类型。

旁注,因为您正在尝试了解可能的内容:

如果您将BaseImpl定义为BaseImpl<T extends Number> implements MyInterface<T>,则可以从类T的反射数据中提取BaseImpl的上限,即您{&lt; 1}}。 d能够知道T必须是Number或子类型(或接口情况下的实现)。