Java 类型系统

类型系统与反射

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容