类型系统与反射
JVM会为每个加载到内存的类创建了一个Class对象,而我们可以通过Class对象来获取类中的Field(字段)、Constructor(构造方法)、Method(普通方法)、Parameter(参数)、Modifier(类、变量、方法等的修饰符)等几乎所有的类元数据信息。这也就是Java反射的基础,为Java带来了极大的灵活性和通用性。
在泛型类型(Generic Type)出现之前,我们能够通过反射来获取Class的元数据信息。但是泛型类型引入后,由于类型擦除(Type erase),在编译后泛型类型信息被除掉了,导致原本类似List<Integer>和List<String>不同的类型都编译成了同一个Class对象。
类型擦除是Java引入泛型的一种折中方案,由于需要兼容老版本的JVM,所以通过类型擦除将类型信息在编译期去除,保证JVM对于接受的泛型类和普通类没有任何差异。
既然通过普通反射无法获取泛型类型的元数据信息了,Java索性做了一层更高层的抽象,也就是Type。
Type接口
Type是Java编程语言的中所有类型的通用超级接口,包括原始类型(raw types)、参数化类型(Parameterized types)、类型变量(Type variables)和基本类型(primitive types)。
下面是Type源码,可以看到只有一个GetTypeName
方法,用于返回描述此类型的字符串。
package java.lang.reflect;
public interface Type {
default String getTypeName() {
return toString();
}
}
对于Type接口直接实现类只有Class,除了Class实现类外,还有四个子接口(ParameterizedType、TypeVariable、GenericArrayType和WildcardType)扩展了Type接口。
Class、ParameterizedType、TypeVariable、GenericArrayType和WildcardType组成了Java编程类型体系。
WildcardType虽然是Type的子接口,但是WildcardType并不是java中类型中的一种。
Class类型
Class类型代表Java程序中正在运行的类(Class)和接口(Interface),包括枚举(Enum)、注解(Annotation)、数组(比如int []、List[]等)以及原始基础类型(byte、int、short、double等八大基础类型)等。可以说在没有泛型类型之前,Class类型可以说是就包括了Java的所有类型。
上面提到的Type类型中,原始类型(raw type)和基本类型(primitive type)都属于Class类型。
Class类没有提供public构造方法,Class的构造是由JVM自动构造或者通过ClassLoader的defineClass来构造。
通过调用Class的getTypeName会打印对应类的全类名。
public void testJavaType() {
String str = new String("Hello world");
System.out.println(str.getClass().getTypeName());
}
输出结果:
java.lang.String
ParameterizedType类型
ParameterizedType(参数类型)正如其名字一样,代表具有类型参数的类型,比如List<T>、Map<K, V>等具有类型参数的类型都属于ParameterizedType。
ParameterizedType源码如下:
public interface ParameterizedType extends Type {
/**
* 返回类型中具体的参数化类型,比如Map<String, Integer>, 则返回的是[java.lang.String, java.lang.Integer]组成的类型数组
*/
Type[] getActualTypeArguments();
/**
* 返回类型参数的原始类型,比如List<String>中的List、Map<String, Integer>中的Map。
*/
Type getRawType();
/**
* 该方法是用于内部类的,用于获取内部类所属的类型。比如Map.Entry,Entry属于Map类型的内部类,所以返回Map
*/
Type getOwnerType();
}
下面是一个用于测试Parameterized的测试类,可以看到实际只有v1、v2、v4和v5是ParameterizedType。
public class ParameterizedTypeTest {
private Map<String, Integer> v1;
private List<String> v2;
private List v3;
private Class<?> v4;
private Map.Entry<String, ParameterizedTypeTest> v5;
private String v6;
public static void main(String[] args) {
Field[] fields = ParameterizedTypeTest.class.getDeclaredFields();
for (Field field : fields) {
if (field.getGenericType() instanceof ParameterizedType) {
System.out.println("====" + field.getName() + ":" + field.getType() + "====");
ParameterizedType fieldType = (ParameterizedType) field.getGenericType();
System.out.println("getTypeName: " + fieldType.getTypeName());
System.out.println("getRawType: " + fieldType.getRawType());
System.out.println("getOwnerType: " + fieldType.getOwnerType());
System.out.println("getActualTypeArguments: " + Arrays.toString(fieldType.getActualTypeArguments()));
}
}
}
}
输出:
====v1:interface java.util.Map====
getTypeName: java.util.Map<java.lang.String, java.lang.Integer>
getRawType: interface java.util.Map
getOwnerType: null
getActualTypeArguments: [class java.lang.String, class java.lang.Integer]
====v2:interface java.util.List====
getTypeName: java.util.List<java.lang.String>
getRawType: interface java.util.List
getOwnerType: null
getActualTypeArguments: [class java.lang.String]
====v4:class java.lang.Class====
getTypeName: java.lang.Class<?>
getRawType: class java.lang.Class
getOwnerType: null
getActualTypeArguments: [?]
====v5:interface java.util.Map$Entry====
getTypeName: java.util.Map$Entry<java.lang.String, io.ray.state.ParameterizedTypeTest>
getRawType: interface java.util.Map$Entry
getOwnerType: interface java.util.Map
getActualTypeArguments: [class java.lang.String, class io.ray.state.ParameterizedTypeTest]
注意:
getActualTypeArguments只返回第一层<>内的类型参数。比如上面的Map.Entry<String, ParameterizedTypeTest> 中返回是String和ParameterizedTypeTest类型;在比如上面的Class<?>中返回的是通配符?。
TypeVariable类型
TypeVariable类型变量类型,是指T v1中的T就是类型变量。
TypeVariable源码如下:
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
/**
* 获取类型变量的上边界类型
*/
Type[] getBounds();
/**
* 获取声明该类型变量的实体
*/
D getGenericDeclaration();
/**
* 获取类型变量在源码中的声明名称
*/
String getName();
/**
* 返回注解类型数组,和上边界类型一一对应(没太懂用来干嘛的)
*/
AnnotatedType[] getAnnotatedBounds();
}
下面是用于测试TypeVariable类型的测试用例,可以看到实际只有v1和v2属于TypeVariable类型。
public class TypeVariableTypeTest<K extends Number & Serializable, V> {
private K v1;
private V v2;
private K[] v3;
private List<K> v4;
public static void main(String[] args) {
Field[] fields = TypeVariableTypeTest.class.getDeclaredFields();
for (Field field : fields) {
if (field.getGenericType() instanceof TypeVariable) {
System.out.println("====" + field.getName() + ":" + field.getType() + "====");
TypeVariable fieldType = (TypeVariable) field.getGenericType();
System.out.println("getTypeName: " + fieldType.getTypeName());
System.out.println("getBounds: " + Arrays.toString(fieldType.getBounds()));
System.out.println("getName: " + fieldType.getName());
System.out.println("getGenericDeclaration: " + fieldType.getGenericDeclaration());
System.out.println("getAnnotatedBounds: " + Arrays.toString(fieldType.getAnnotatedBounds()));
}
}
}
}
输出:
====v1:class java.lang.Number====
getTypeName: K
getBounds: [class java.lang.Number, interface java.io.Serializable]
getName: K
getGenericDeclaration: class io.ray.state.TypeVariableTypeTest
getAnnotatedBounds: [sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@2077d4de, sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@7591083d]
====v2:class java.lang.Object====
getTypeName: V
getBounds: [class java.lang.Object]
getName: V
getGenericDeclaration: class io.ray.state.TypeVariableTypeTest
getAnnotatedBounds: [sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@77a567e1]
我们以K v1做详细分析:
v1对应的getTypeName为K,也就是声明类型。
v1类型变量对应上边界为Number和Serializable,因为K在类定义中指定了上边界为Number和Serializable。
v1对应getGenericDeclaration为TypeVariableTypeTest,因为K是在TypeVariableTypeTest中定义的。
GenericArrayType类型
GenericArrayType即泛型数组类型,比如T[]、List<T>[] 这类都属于泛型数组类型。
GenericArrayType源码如下:
public interface GenericArrayType extends Type {
/**
* 返回泛型数组中元素的数据类型,比如T[] 返回的是T。
*/
Type getGenericComponentType();
}
下面是用于测试GenericArrayType的测试类,可以看到只有v2和v4为GenericArrayType类型。
public class GenericArrayTypeTest<T> {
private T v1;
private T[] v2;
private List<T> v3;
private List<T>[] v4;
private GenericArrayTypeTest[] v5;
public static void main(String[] args) {
Field[] fields = GenericArrayTypeTest.class.getDeclaredFields();
for (Field field : fields) {
if (field.getGenericType() instanceof GenericArrayType) {
System.out.println("====" + field.getName() + ":" + field.getType() + "====");
GenericArrayType fieldType = (GenericArrayType) field.getGenericType();
System.out.println("getTypeName: " + fieldType.getTypeName());
System.out.println("getGenericComponentType: " + fieldType.getGenericComponentType());
}
}
}
}
输出:
====v2:class [Ljava.lang.Object;====
getTypeName: T[]
getGenericComponentType: T
====v4:class [Ljava.util.List;====
getTypeName: java.util.List<T>[]
getGenericComponentType: java.util.List<T>
我们以List<T>[] v4来说明:
v4对应的getTypeName为参数声明类型java.util.List<T>[]。
v4对应的component type为数组元素实际类型List<T>。
WildcardType类型
WildcardType即通配符类型,或称为泛型表达式类型。WildcardType虽然是Type的子接口,但是WildcardType并不是java类型中的一种,它仅仅是类似T extend Number、? super T这类的类型表达式。
WildcardType源码如下:
public interface WildcardType extends Type {
/**
* 获取泛型表达式中的上边界,也就是我们通过extends定义的上边界
*/
Type[] getUpperBounds();
/**
* 获取泛型表达式中的下边界,也就是我们通过super定义的下边界
*/
Type[] getLowerBounds();
}
下面是用于测试WildcardType的测试用例,可以看到v1、v2、v3、v4的的输出。
public class WildcardTypeTest {
private List<? extends Number> v1;
private List<? super Integer> v2;
private Map<? extends String, ? super Integer> v3;
private Class<?> v4;
private List v5;
public static void main(String[] args) {
Field[] fields = WildcardTypeTest.class.getDeclaredFields();
for (Field field : fields) {
Type fieldType = field.getGenericType();
if (fieldType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) fieldType;
Type[] subTypes = parameterizedType.getActualTypeArguments();
System.out.println("====" + field.getName() + ":" + field.getType() + "====");
for (Type type : subTypes) {
if (type instanceof WildcardType) {
WildcardType wildcardType = (WildcardType)type;
System.out.println("getTypeName: " + wildcardType.getTypeName());
System.out.println("getUpperBounds: " + Arrays.toString(wildcardType.getUpperBounds()));
System.out.println("getLowerBounds: " + Arrays.toString(wildcardType.getLowerBounds()));
}
}
}
}
}
}
下面是输出,从输出可以看到,如果没有设置extends上限,默认是Object作为上限。
====v1:interface java.util.List====
getTypeName: ? extends java.lang.Number
getUpperBounds: [class java.lang.Number]
getLowerBounds: []
====v2:interface java.util.List====
getTypeName: ? super java.lang.Integer
getUpperBounds: [class java.lang.Object]
getLowerBounds: [class java.lang.Integer]
====v3:interface java.util.Map====
getTypeName: ? extends java.lang.String
getUpperBounds: [class java.lang.String]
getLowerBounds: []
getTypeName: ? super java.lang.Integer
getUpperBounds: [class java.lang.Object]
getLowerBounds: [class java.lang.Integer]
====v4:class java.lang.Class====
getTypeName: ?
getUpperBounds: [class java.lang.Object]
getLowerBounds: []
总结
Java因为扩展泛型类型,引入了Type体系,Type是Java编程语言的类型系统超级接口。Type下由Class、TypeVariable、ParameterizedType、GenericArrayType和WildcardType组成了Java编程语言的类型系统。Class类型是指未出现泛型前所有类型,包括数组、接口、枚举、注解等等,都属于Class 原始类型;TypeVariable是指泛型类型中的泛型变量,一般主要用于类中定义了泛型标识T,类中字段使用T作为字段类型的变量,比如T v1;GenericArrayType是指泛型类型的数组,和TypeVariable类似,但是是泛型变量的数组,比如T[] v1。ParameterizedType参数化类型是指类型中具有泛型参数的变量,ParameterizedType需要和TypeVariable区分,TypeVariable是指纯泛型类型变量,而ParameterizedType是指类型中包含类型变量的类型,比如List<T>这种,类型由List和泛型T两部分组成。
看了上面的Java类型系统,对我们日常工作又有哪些好处呢?
个人理解主要两方面,一方面Java类型系统属于Java的高级特性,能够使我们更加透彻的理解Java中类型、泛型、反射等。另一方面,对于一些序列化框架、RPC框架、甚至一些引擎框架的类型系统(比如我们正在做的RayState类型系统)都会用到相关知识。
参考资料
https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Type.html
https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html
https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/GenericArrayType.html
https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/ParameterizedType.html
https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/TypeVariable.html
https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/WildcardType.html