美文网首页
Java 类型系统

Java 类型系统

作者: 零度沸腾_yjz | 来源:发表于2021-11-08 19:36 被阅读0次

    类型系统与反射

    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接口。

    image.png

    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

    https://blog.csdn.net/z69183787/article/details/54314169

    https://www.jianshu.com/p/39cc237ad815

    相关文章

      网友评论

          本文标题:Java 类型系统

          本文链接:https://www.haomeiwen.com/subject/ykwkzltx.html