目录

  • JAVA的类型(Type)体系
  • 1.Type 体系的历史
  • 2.自定义工具方法
  • 3. 各种 Type
  • 参数化类型:ParameterizedType
  • getRawType(): Type
  • getOwnerType(): Type
  • getActualTypeArguments(): Type[ ]
  • GenericArrayType:泛型数组类型
  • TypeVariable:类型变量</font>
  • WildcardType: 通配符类型
  • 新类型的新方法
  • 泛型和类型擦除
  • 先有子(Class),后有父(Type)
  • 1. 泛型相关新方法
  • Method 的 getGenericParameterTypes 方法
  • Method 的 getGenericReturnType 方法
  • Class 的 getGenericSuperclass 方法


JAVA的类型(Type)体系

1.Type 体系的历史

提示
实际上,是先后 Class,而后有 Type 。也就是说,一开始 Java 并没有 “类型体系” 这样的概念。
是因为引入了泛型概念之后,为了将泛型概念引入 Java,并作出向后兼容,
从而为 Class “补”了一个 Type 祖先和其它兄弟, 从而完善了整个体系。

在早期的 Java (5.0 之前)中所有的类型都有一个 Class 对象,包括基本类型和自定义类型:

Student.class
Teacher.class
String.class
Integer.class
Double.class
Boolean.class
int.class 
int[].class
double.class 
double[].class
boolean.class 
boolean[].class
...

Class 对象中包含了当前类型的定义信息,它是 Java 反射的基础。通过一个类型的 Class 对象,你可以查询得到这个类型有哪些域(Field),哪些方法(Method),哪些构造器(Constructor)等信息。

但是,在 JDK 5.0 引入泛型概念之后,Java 官方发现,新引入的泛型相关的一些类型,它们不适用上面我们所说的“所有的类型都有一个 Class 对象”这句话。

这些泛型相关的类型的“那个对象”,不能归类于是 Class 对象这个概念之下。它们的“那个对象”既和 Class 对象有相似的地方,又和 Class 对象有所区别。

对此,Java 官方抽取了它们和 Class 的相同点,提炼出 Type 概念,并补全了其它的类型:

Type
├── Class
├── ParameterizedType
├── TypeVariable
├── WildcardType
└── GenericArrayType

Type 和它的子接口、实现类(Class、ParameterizedType、TypeVariable、WildcardType、GenericArrayType)共同组成了 Java 的类型体系。
现在:
所有和泛型没有半毛钱关系的类型,都有一个 Class 类型的对象与之对应。例如,String 类型、String[] 类型。
所有和泛型沾边的类型,根据其具体情况,都有一个 Type 类型的子类型的对象与之对应。例如,List<String> 类型、T[] 类型。
这里有一个比较典型的示例:

String[] 类型的类型对象是 Class;
List<String> 类型的类型对象是 Type 类型的子类型 ParameterizedType;
List<String>[] 类型的类型对象是 Type 类型的子类型 GenericArrayType。

2.自定义工具方法

为了更方便地检测 Type 的具体类型,我们可以准备一个如下的简单的方法:

public static String getTypeName(Type type) {
    if (type instanceof Class)
        return "Class";             // just like "String"
    else if (type instanceof TypeVariable)
        return "TypeVariable";      // just like "T"
    else if (type instanceof ParameterizedType)
        return "ParameterizedType"; // just like "List<String>";
    else if (type instanceof GenericArrayType)
        return "GenericArrayType";  // just like "T[]";
    else
        return "something wrong";   // 理论上不该如此
}

3. 各种 Type

public class Student<T extends Number> {  
    public List<T> a = new ArrayList<>();  
    public T b;  
    public List<String>[] c;  
    public T[] d;
}

参数化类型:ParameterizedType

参数化类型即我们通常所说的泛型类型,一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。

那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

public static void demoA() {  
    Field aField = null;  
    Field[] fields = Student.class.getDeclaredFields();  
    for (Field field : fields) {  
        if (field.getName().equals("a")) {  
            aField = field;  
            break;  
        }  
    }  
  
    ParameterizedType aType = castToParameterizedType(aField.getGenericType());  
    System.out.println("           rawType: " + aType.getRawType());  
    System.out.println("         ownerType: " + aType.getOwnerType());  
    for (Type actualTypeArgument : aType.getActualTypeArguments()) {  
        System.out.println("ActualTypeArgument: " + actualTypeArgument);  
    }  
}

ParameterizedType 有 3 个常见的重要的方法:

getRawType(): Type

该方法的作用是返回当前的 ParameterizedType 的类型。如一个List,返回的是 List 的 Type,即,返回的是不带反省参数部分的剩下部分的那个类型。

如果你当初“求”类型的时候,调用的不是 .getGenericType()方法,而是 .getType() 方法,.getType() 方法所返回的类型就是这个 RawType 。

getOwnerType(): Type

有些泛型类是内部类,最典型的就是 Map.Entry 。

ParameterizedType 类型的 .getOwnerType() 方法返回的就是这样的内部类类型的泛型对象所在的外部类的类型(好拗口…)

如果,Student 中存在一个 public Map.Entry<String, String> x; 属性,对它调用 .getOwnerType()方法,得到的就是 Map 。

如果泛型类是正常的、普通类,非内部类,这个方法会返回 null 。

getActualTypeArguments(): Type[ ]

该方法返回参数化类型<>中的实际参数类型。

如果,Student 中存在一个 public Map<String, Date> x; 这个 ParameterizedType 返回的是 String 和 Date 类的全限定类名的 Type 对象的 Array 。

注意: 该方法只返回最外层的<>中的类型,无论该<>内有多少个<>。

另外,如果属性 x 的类型不是一个确定的泛型类型,而是类似于 public List<T> x 这样使用了泛型变量,那么通过 getActualTypeArguments() 方法,你获得就是那个泛型变量 T 。

GenericArrayType:泛型数组类型

如果,Student 类中有一个 public T[ ] x 或者是 public List<String>[ ] x 这样的属性时,这些属性的类型的类型就是 GenericArrayType 。

GenericArrayType 的关键方法是 .getGenericComponentType() 方法。它会返回这个泛型数组的成员的类型。

在上例中,就是 T 和 List<String> 。

注意:无论从左向右有几个[]并列,这个方法仅仅脱去最右边的[]之后剩下的内容就作为这个方法的返回值。

这里我们知道,从理论上来说,它的组成元素的类型的类型一定是 ParameterizedType 或 TypeVariable 。

TypeVariable:类型变量

如果,Student 类中有一个 public T x 这样的属性时,属性的类型的类型就是 TypeVariable 。

另外,前面我们说过,对于属性 public T[] x 来说,它的类型的类型是 GenericArrayType 类型,而它的组成元素的类型的类型也就是 TypeVariable 。

范型信息在编译时会被转换为一个特定的类型, 而 TypeVariable 就是用来反映在 JVM 编译该泛型前的信息。

getBounds(): Type[]
返回当前类型的上边界,如果没有指定上边界,则默认为Object。

getName(): String
返回当前类型的类名

getGenericDeclaration(): D
返回当前类型所在的类的 Type 。

WildcardType: 通配符类型

表示通配符类型,比如 <?>, <? Extends Number>等
getLowerBounds(): Type[] 得到下边界的数组
getUpperBounds(): Type[] 得到上边界的 type 数组
注:如果没有指定上边界,则默认为 Object,如果没有指定下边界,则默认为 String。

新类型的新方法

ParameterizedType 的 getActualTypeArguments Class 的 getGenericSuperclass 方法返回的是泛型父类,它的类型是 Type 。

实际上通过 instanceof 运算符,我们可以判断出泛型父类的实际信息是 ParameterizedType 类型。

而 ParameterizedType 类型有一个 getActualTypeArguments 方法,它能返回泛型父类的所使用的泛型参数。

ParameterizedType superclass = (ParameterizedType)StringLinkedList.class.getGenericSuperclass();
for (Type cur : superclass.getActualTypeArguments()) {
    System.out.println(cur);    // 这个例子中只有一个:String
}

泛型和类型擦除

Type 是 Java 语言中所有类型的公共父接口。这就是最官方的解释。
Class 就是 Type 的一个直接实现类。Type 和 Class,以及 Type 的其它子接口(和实现类)组成了 Java 的类型体系。

先有子(Class),后有父(Type)

Class 对象中包含了当前类型的定义信息,它是 Java 反射的基础。通过一个类型的 Class 对象,你可以查询得到这个类型有哪些域,哪些方法,哪些构造函数等信息。
在这个时候,一个类的 Class 对象中"包含"了足够多的关于这个类的相关信息。
例如对于下面的类:

public class T1 {  
    public int f1;  
    public int[] f2;
}

你是使用反射方法能获得 f1 和 f2 两个属性的相关信息:

T1 x = new T1();  
System.out.println(x.getClass().getField("f1"));  
System.out.println(x.getClass().getField("f2"));

但是泛型的概念出现后,情况就变复杂了,对于涉及到泛型的数据类型,原本的一些方法(1.5 之前出现的方法),所返回的信息中无法"囊括"泛型相关信息。

例如,如果我们上例中的 T1 类变成了一个泛型类:

public class T1<E> {  
    public int f1;  
    public int[] f2;  
  public E[] f3;  
  public Object[] f4;
}

仍然使用上面的方法。

1. 泛型相关新方法

Method 的 getGenericParameterTypes 方法

getGenericParameterTypes 方法是 getParameterTypes 方法的升级版。

Method 的 getGenericParameterTypes 方法的作用和上面的 getParameterTypes 方法类似,不过由于它是一个「新」方法,因此,它的返回结果中会保留泛型信息。

还是同样的上述方法:

public static <T> void demo(T arg0, T[] arg1, String arg2) {
    ...
}

getGenericParameterTypes 方法的返回值是 Type 的数组(而不是 Class 数组)。

它的三个元素的分别是

  • 第一个参数类型 T ,类型为 TypeVariable
  • 第二个参数类型 T [ ], 类型为 GenericArrayType
  • 第三个参数类型 String, 类型为 Class

Method 的 getGenericReturnType 方法

getGenericReturnType 方法是 getReturnType 方法的升级版

对于同样的方法,Method 的 getGenericReturnType 方法能识别出返回值类型的泛型信息。

它的返回是 T,其类型为 TypeVariable 。

Class 的 getGenericSuperclass 方法

Class 的 getGenericSuperclass 功能同 getSuperclass,不过它会保留父类的泛型信息。

StringLinkedList.class.getGenericSuperclass()   // LinkedList<String>