Java反射-泛型

时间:2020-01-09 10:36:19  来源:igfitidea点击:

我经常在文章和论坛中读到,所有Java Generics信息在编译时都会被删除,因此我们无法在运行时访问其中的任何信息。但是,这并非完全正确。在少数情况下,可以在运行时访问泛型信息。这些案例实际上涵盖了我们对Java泛型信息的一些需求。本文解释了这些情况。

泛型反射法则

使用Java泛型通常会出现以下两种情况之一:

  • 将类/接口声明为可参数化的。
  • 使用可参数化的类。

当我们编写一个类或者接口时,我们可以指定它应该是可参数化的。 java.util.List接口就是这种情况。除了创建Object列表之外,还可以参数化java.util.List来创建诸如String的列表,如下所示:

List<String> myList = new ArrayList<String>();

当在运行时通过反射检查可参数化类型本身时,例如java.util.List,无法知道已将参数化为哪种类型。对象本身不知道将其参数化的对象。

但是,对对象的引用知道它所引用的类型,包括通用类型。也就是说,如果它不是局部变量。如果对象由对象中的字段引用,则可以通过反射查看Field声明,以获得有关该字段声明的泛型类型的信息。

如果通过方法中的参数引用对象,则可能会发生同样的情况。通过该方法的"参数"对象(Java反射对象),我们可以看到该参数声明为哪种泛型类型。

最后,我们还可以查看方法的返回类型,以了解将其声明为哪种泛型类型。同样,我们无法从返回的实际对象中看到它。我们需要通过反射查看方法声明,以了解其声明的返回类型(包括泛型类型)。

总结一下:我们只能从引用的声明(字段,参数,返回类型)中看到这些引用所引用的对象将具有哪种泛型类型。我们无法从对象本身看到它。

以下各节将仔细研究这些情况。

通用方法返回类型

如果获得了java.lang.reflect.Method对象,则可以获取有关其通用返回类型的信息。我们可以在文本" Java泛型:方法"中阅读如何获取"方法"对象。这是带有方法的实例类,该方法具有参数化的返回类型:

public class MyClass {

  protected List<String> stringList = ...;

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

在此类中,可以获得getStringList()方法的通用返回类型。换句话说,可以检测到getStringList()返回的是List <String>,而不仅仅是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实例的一项。 "类"实现"类型"接口。

通用方法参数类型

我们还可以在运行时通过Java Reflection访问参数类型的通用类型。这是一个示例类,其中带有以参数化的" List"作为参数的方法:

public class MyClass {
  protected List<String> stringList = ...;

  public void setStringList(List<String> 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.String的Class实例的项。 "类"实现"类型"接口。

通用字段类型

也可以访问公共字段的通用类型。字段是类成员变量,可以是静态变量也可以是实例变量。我们可以在文本" Java泛型:字段"中阅读有关获取Field对象的信息。这是前面的示例,带有一个名为stringList的实例字段。

public class MyClass {
  public List<String> 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实例的一项。 "类"实现"类型"接口。