Java Type类型

1 Type接口

Java中,JVM会为每一个加载到内存中的类型创建一个Class对象。通过Class对象我们可以获取FieldConstructorMethodParameterModifier等几乎所有的类的元数据信息,这就是Java中反射的基础,为Java开发提供了很大的灵活性和通用性,是Java语言的一大亮点。

但是遇到泛型类(Generic Class)时,Class就不灵光了。在类型擦除(Type erase)后,Class丢失了泛型类型信息。比如,List<Integer>List<String>这两个类型,他们的Class对象是同一个,但是这两个类型又截然不同,这两个类型的变量不能相互赋值,也不能把一个List中的元素存入另一个List

其实从Java 1.5开始,Java提供了一个Type接口。Type接口是所有类型的公共父接口,包括类和接口(raw types)、参数化类型(parameterized types)、数组类型(array types)、类型变量(type variables)和基本类型(primitive types),可以说,Type是Java语言中的顶级类型接口。Class就实现了Type接口,List<Integer>List<String>两个类型的Type信息是不一样的。

有了Type类型,Java开发可以更加灵活,可以写出更加通用的代码。

我们先看看Type接口的定义,它只有一个方法,就是获取Type的字符串名字。

public interface Type {

    default String getTypeName() {
        return toString();
    }
}

下面这些类和接口实现或扩展了Type接口:

graph BT

B[Class]-->type[Type]
C[ParameterizedType]-->type
D[TypeVariable]-->type
E[GenericArrayType]-->type
F[WildcardType]-->type

从上图可以看到,除了Class实现了Type接口,还有四个接口(ParameterizedType、TypeVariable、GenericArrayType、WildcardType)扩展了Type接口。

1.1 Class类

这个大家已经比较熟了,就不多介绍了。它的getTypeName()返回的内容就是Class的全限定域名(fully qualified domain name,FQDN)

1.2 ParameterizedType接口

ParameterizedType接口,跟它的名字一样,参数化类型,表示该类型带有类型参数,比如List<String>这种。它的定义如下:

public interface ParameterizedType extends Type {
    Type[] getActualTypeArguments();
    Type getRawType();
    Type getOwnerType();
}

getActualTypeArguments()方法返回实际的参数类型,因为一个类型可以有多个类型参数,所以是一个数组。比如List<String>类型的这个方法返回的是String类的Class对象(还记得Class实现了Type接口吗?)

getRawType()方法返回擦除类型参数后的类型,比如List<String>类型的这个方法返回的是List类的Class对象

getOwnerType()方法,如果当前类型定义在另一个类型的内部,则返回外部的类型;否则,则是一个顶层类型(top-level type),返回null。比如List<String>类型的这个方法返回的是null,但是Map.Entry<String, String>类型的这个方法返回的就是Map类的Class对象,因为Entry类定义在Map类的内部

ParameterizedType接口可能是我们使用的最多的Type子接口,毕竟我们使用Type类型,往往是希望知道类型的泛型参数的具体化后的类型。当然ParameterizedType的泛型参数不一定具体化了,它只是表明这个类型包含类型参数。泛型参数是否具体化,可以通过getActualTypeArguments()方法返回的Type类型是Class类型还是其他Type类型来确定。不过这里还存在一个嵌套的问题,比如List<List<String>>,它的ActualTypeArgumentList<String>,还是一个ParameterizedType

1.3 TypeVariable接口

跟它的名字一样,表示这是一个类型变量。比如类型List<T>中的T就是一个TypeVariable。它的定义如下:

public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
    Type[] getBounds();
    D getGenericDeclaration();
    String getName();
    AnnotatedType[] getAnnotatedBounds();
}    

通过TypeVariable我们主要通过getBounds()getName()方法获取它的边界和名字。比如List<T extends Person>类型,它的ActualTypeArgumentTypeVariable类型,这个TypeVariable的名字是T,边界是Person Class类型。

1.4 GenericArrayType接口

泛型数组类型接口,它的定义如下:

public interface GenericArrayType extends Type {
    Type getGenericComponentType();
}

GenericArrayType接口只有一个getGenericComponentType()方法,返回数组元素的类型,如List<String>[]类型返回的结果是List<String>类型,他是一个ParameterizedType

1.5 WildcardType接口

表示泛型的通配符(?)类型,比如List<?>中的?。它的定义如下:

public interface WildcardType extends Type {
    Type[] getUpperBounds();
    Type[] getLowerBounds();
}

WildcardType接口提供了两个接口,分别获取通配符?的上下边界。比如List<? extends Person>的上边界是Person ClassList<? super Person>的下边界是Person Class

2 获取Type接口信息

我们可以通过类的全限定域名或类的对象获取Class对象,Type则可以通过ClassFieldMethodConstructorParameter这些表示类的元数据对象获取,因为FieldMethodConstructorParameter只能通过Class间接获取,所以Type信息都是从Class获取的。

上述对象获取Class信息的接口,接口名前带有generic前缀的往往就是获取相应Type信息的接口

下面通过一些实例观察获取Type的一些接口信息,递归打印Type信息的方法:

    public static void printTypeInfo(final Type type) {

        log.info("type.getTypeName(): {}", type.getTypeName());
        if (type instanceof Class) {
            Class c = (Class) type;
            log.info("type is Class");
        } else if (type instanceof ParameterizedType) {
            ParameterizedType t = (ParameterizedType) type;
            log.info("type is ParameterizedType");

            Type[] actualTypeArguments = t.getActualTypeArguments();
            log.info("ParameterizedType.getActualTypeArguments(): {}", Arrays.toString(actualTypeArguments));
            for (int i = 0; i < actualTypeArguments.length; i++) {
                printTypeInfo(actualTypeArguments[i]);
            }

            Type rawType = t.getRawType();
            log.info("ParameterizedType.getRawType(): {}", rawType);
            printTypeInfo(rawType);

            Type ownerType = t.getOwnerType();
            log.info("ParameterizedType.getOwnerType(): {}", ownerType);
            if (ownerType != null) {
                printTypeInfo(ownerType);
            }

        } else if (type instanceof TypeVariable) {
            TypeVariable t = (TypeVariable) type;
            log.info("type is TypeVariable");

            String name = t.getName();
            log.info("TypeVariable.getName(): {}", name);

            Type[] bounds = t.getBounds();
            log.info("TypeVariable.getBounds(): {}", Arrays.toString(bounds));
            if (bounds != null) {
                for (int i = 0; i < bounds.length; i++) {
                    printTypeInfo(bounds[i]);
                }
            }

        } else if (type instanceof GenericArrayType) {
            GenericArrayType t = (GenericArrayType) type;
            log.info("type is GenericArrayType");
            Type componentType = t.getGenericComponentType();
            log.info("GenericArrayType.getGenericComponentType(): {}", componentType);
            printTypeInfo(componentType);
        } else if (type instanceof WildcardType) {
            WildcardType t = (WildcardType) type;
            log.info("type is WildcardType");

            Type[] upperBounds = t.getUpperBounds();
            log.info("WildcardType.getUpperBounds(): {}", Arrays.toString(upperBounds));
            if (upperBounds != null) {
                for (int i = 0; i < upperBounds.length; i++) {
                    printTypeInfo(upperBounds[i]);
                }
            }

            Type[] lowerBounds = t.getLowerBounds();
            log.info("WildcardType.getLowerBounds(): {}", Arrays.toString(lowerBounds));
            if (lowerBounds != null) {
                for (int i = 0; i < lowerBounds.length; i++) {
                    printTypeInfo(lowerBounds[i]);
                }
            }
        }

    }

2.1 Class

用来测试的类:

public interface Interface1 {
}

public interface Interface2<T1, T2> {
}

public class Class2<T> {
}

public class Class1<T, S> extends Class2<S> implements Interface1, Interface2<String, T> {
}

2.1.1 获取父类的Type: getGenericSuperclass()

测试代码:

    Class c = Class1.class;

    Type genericSuperclass = c.getGenericSuperclass();
    log.info("=====begin print genericSuperclass");
    if (genericSuperclass != null) {
        printTypeInfo(genericSuperclass);
    }
    log.info("=====finish print genericSuperclass");

打印结果:

=====begin print genericSuperclass
type.getTypeName(): com.javatest.generic.Class2<S>
type is ParameterizedType
ParameterizedType.getActualTypeArguments(): [S]
type.getTypeName(): S
type is TypeVariable
TypeVariable.getName(): S
TypeVariable.getBounds(): [class java.lang.Object]
type.getTypeName(): java.lang.Object
type is Class
ParameterizedType.getRawType(): class com.javatest.generic.Class2
type.getTypeName(): com.javatest.generic.Class2
type is Class
ParameterizedType.getOwnerType(): null
=====finish print genericSuperclass

2.1.2 获取父接口的Type: getGenericInterfaces()

测试代码:

    Type[] genericSuperInterfaces = c.getGenericInterfaces();
    log.info("=====begin print genericSuperInterfaces");
    if (genericSuperInterfaces != null) {

        for (int i = 0; i < genericSuperInterfaces.length; i++) {
            log.info("=====begin print genericSuperInterfaces, i={}", i);
            printTypeInfo(genericSuperInterfaces[i]);
            log.info("=====finish print genericSuperInterfaces i={}", i);
        }
    }
    log.info("=====finish print genericSuperInterfaces");

打印结果:

=====begin print genericSuperInterfaces
=====begin print genericSuperInterfaces, i=0
type.getTypeName(): com.javatest.generic.Interface1
type is Class
=====finish print genericSuperInterfaces i=0
=====begin print genericSuperInterfaces, i=1
type.getTypeName(): com.javatest.generic.Interface2<java.lang.String, T>
type is ParameterizedType
ParameterizedType.getActualTypeArguments(): [class java.lang.String, T]
type.getTypeName(): java.lang.String
type is Class
type.getTypeName(): T
type is TypeVariable
TypeVariable.getName(): T
TypeVariable.getBounds(): [class java.lang.Object]
type.getTypeName(): java.lang.Object
type is Class
ParameterizedType.getRawType(): interface com.javatest.generic.Interface2
type.getTypeName(): com.javatest.generic.Interface2
type is Class
ParameterizedType.getOwnerType(): null
=====finish print genericSuperInterfaces i=1
=====finish print genericSuperInterfaces

2.1.3 获取类型参数的Type: getTypeParameters()

测试代码:

    TypeVariable[] typeVariables = c.getTypeParameters();
    log.info("=====begin print typeVariables");
    if (typeVariables != null) {

        for (int i = 0; i < typeVariables.length; i++) {
            log.info("=====begin print typeVariables, i={}", i);
            printTypeInfo(typeVariables[i]);
            log.info("=====finish print typeVariables i={}", i);
        }
    }
    log.info("=====finish print typeVariables");

打印结果:

=====begin print typeVariables
=====begin print typeVariables, i=0
type.getTypeName(): T
type is TypeVariable
TypeVariable.getName(): T
TypeVariable.getBounds(): [class java.lang.Object]
type.getTypeName(): java.lang.Object
type is Class
=====finish print typeVariables i=0
=====begin print typeVariables, i=1
type.getTypeName(): S
type is TypeVariable
TypeVariable.getName(): S
TypeVariable.getBounds(): [class java.lang.Object]
type.getTypeName(): java.lang.Object
type is Class
=====finish print typeVariables i=1
=====finish print typeVariables

2.2 Field

用来测试的类

public class Class2<T> {
    
    String str;
    List<String> strList1;
    List<T> strList2;
    T t1;
    
}

2.2.1 类属性的Type: getGenericType()

测试代码:

    Class c = Class2.class;

    log.info("=====begin pring field str");
    Field f = c.getDeclaredField("str");
    Type type = f.getGenericType();
    printTypeInfo(type);
    log.info("=====finish pring field str");

    log.info("=====begin pring field strList1");
    f = c.getDeclaredField("strList1");
    type = f.getGenericType();
    printTypeInfo(type);
    log.info("=====finish pring field strList1");

    log.info("=====begin pring field strList2");
    f = c.getDeclaredField("strList2");
    type = f.getGenericType();
    printTypeInfo(type);
    log.info("=====finish pring field strList2");

    log.info("=====begin pring field t1");
    f = c.getDeclaredField("t1");
    type = f.getGenericType();
    printTypeInfo(type);
    log.info("=====finish pring field t1");

打印结果:

=====begin pring field str
type.getTypeName(): java.lang.String
type is Class
=====finish pring field str
=====begin pring field strList1
type.getTypeName(): java.util.List<java.lang.String>
type is ParameterizedType
ParameterizedType.getActualTypeArguments(): [class java.lang.String]
type.getTypeName(): java.lang.String
type is Class
ParameterizedType.getRawType(): interface java.util.List
type.getTypeName(): java.util.List
type is Class
ParameterizedType.getOwnerType(): null
=====finish pring field strList1
=====begin pring field strList2
type.getTypeName(): java.util.List<T>
type is ParameterizedType
ParameterizedType.getActualTypeArguments(): [T]
type.getTypeName(): T
type is TypeVariable
TypeVariable.getName(): T
TypeVariable.getBounds(): [class java.lang.Object]
type.getTypeName(): java.lang.Object
type is Class
ParameterizedType.getRawType(): interface java.util.List
type.getTypeName(): java.util.List
type is Class
ParameterizedType.getOwnerType(): null
=====finish pring field strList2
=====begin pring field t1
type.getTypeName(): T
type is TypeVariable
TypeVariable.getName(): T
TypeVariable.getBounds(): [class java.lang.Object]
type.getTypeName(): java.lang.Object
type is Class
=====finish pring field t1

2.3 Method

从上面ClassField的例子应该知道怎么回事了,后面不再给出例子,只给出获取Type信息的方法

2.3.1 方法参数的Type: getGenericParameterTypes()

2.3.2 方法返回值的Type: getGenericReturnType()

2.3.3 方法声明的抛出异常的Type: getGenericExceptionTypes()

2.4 Constructor

2.3.1 构造器参数的Type: getGenericParameterTypes()

2.3.2 构造器声明的抛出异常的Type: getGenericExceptionTypes()

2.5 Parameter

2.5.1 参数的Type: getParameterizedType()

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

推荐阅读更多精彩内容