Java反射-范型

我经常在文章或论坛中读到所有的Java范型信息都会在编译时搽除,所以不能在运行时访问范型的任何信息。这并不完全准确。某些案例下可以在运行时访问范型信息,这些案例实际上覆盖了我们对范型信息的一些需求。本文将解释这些案例。

范型反射的使用法则

使用范型通常有2种不同的场景:

  1. 定义一个可参数化的类/接口
  2. 使用可参数化的类

当你编写一个类/接口时,可以指定类/接口是否被参数化。这是java.util.List接口的示例。你可以参数化java.util.List接口去创建一个String类型的list,而不是object list,如下:

List myList = new ArrayList();

当使用反射检视可参数化类型本身时,比如java.util.List,是没有办法得到它的已参数化的类型的。object自身是不知道它参数化的类型的。
然而,object的reference知道它包含的范型引用的类型信息。那就是,如果它不是局部变量。如果一个object被另一个object的field引用,你就可以通过反射查看Field定义,通过field获得范型的类型信息。
同理,如果一个object被一个方法的参数引用。可以通过方法的Parameter,你可以看到方法参数确定范型的类型信息。
最后,你可以通过方法返回值确定反射范型信息。需要强调的是,实际返回object时你不能得到范型信息。你需要通过反射查看方法定义,去获得方法定义的返回值(包含范型信息)。
总结起来就是:你只能通过object的引用查看它的范型信息,而不能通过它自身查看。
下面的部分将仔细研究这些情况。

方法返回范型类型

如你已获得java.lang.reflect.Method对象,那么就可以获得它的返回值的范型。你可以阅读 "Java Generics: Methods"学习如何获得Method对象。下面示例类的方法拥有一个可参数化的返回类型。

public class MyClass {

  protected List stringList = ...;

  public List getStringList(){
    return this.stringList;
  }
}

在这个类中可以获得方法getStringList()的带范型的返回类型。换言之,可以发现方法getStringList()返回List并不仅仅是List。代码如下:

Method method = MyClass.class.getMethod("getStringList", null);

Type returnType = method.getGenericReturnType();

if(returnType instanceof ParameterizedType){
    ParameterizedType type = (ParameterizedType) returnType;
    Type[] typeArguments = type.getActualTypeArguments();
    for(Type typeArgument : typeArguments){
        Class typeArgClass = (Class) typeArgument;
        System.out.println("typeArgClass = " + typeArgClass);
    }
}

上面的代码块将输出"typeArgClass = java.lang.String"。Type[]数组typeArguments包含单个元素-java.lang.String类的Class实例。Class实现了Type接口。

方法参数是范型类型

你也可以在运行时通过反射访问方法的范型参数。下面示例类有一个方法拥有一个参数化List作为参数:

public class MyClass {
  protected List stringList = ...;

  public void setStringList(List list){
    this.stringList = list;
  }
}

可以访问方法参数的范型类型,代码如下:

method = Myclass.class.getMethod("setStringList", List.class);

Type[] genericParameterTypes = method.getGenericParameterTypes();

for(Type genericParameterType : genericParameterTypes){
    if(genericParameterType instanceof ParameterizedType){
        ParameterizedType aType = (ParameterizedType) genericParameterType;
        Type[] parameterArgTypes = aType.getActualTypeArguments();
        for(Type parameterArgType : parameterArgTypes){
            Class parameterArgClass = (Class) parameterArgType;
            System.out.println("parameterArgClass = " + parameterArgClass);
        }
    }
}

上述代码将输出"parameterArgType = java.lang.String"。数组Type[] `parameterArgTypes包含单个元素-代表类java.lang.StringClass实例。Class实现了Type`接口。

属性是范型类型

同样的,我们也可以访问公共属性的范型类型。属性是类的成员变量-无论静态或实例变量。你可以获得关于Field的文章"Java Generics: Fields",下方是早些时候的示例代码,拥有一个名为stringList的属性:

public class MyClass {
  public List stringList = ...;
}
Field field = MyClass.class.getField("stringList");

Type genericFieldType = field.getGenericType();

if(genericFieldType instanceof ParameterizedType){
    ParameterizedType aType = (ParameterizedType) genericFieldType;
    Type[] fieldArgTypes = aType.getActualTypeArguments();
    for(Type fieldArgType : fieldArgTypes){
        Class fieldArgClass = (Class) fieldArgType;
        System.out.println("fieldArgClass = " + fieldArgClass);
    }
}

上面的代码将输出"fieldArgClass = java.lang.String"。Type[]数组fieldArgTypes包含的单元素-代表java.lang.String类的Class实例。Class实现了Type接口。

你可能感兴趣的:(反射,java)