Java Type類型

1 Type接口

Java中水泉,JVM會為每一個加載到內(nèi)存中的類型創(chuàng)建一個Class對象抱冷。通過Class對象我們可以獲取Field汗菜、ConstructorMethod摧莽、Parameter庙洼、Modifier等幾乎所有的類的元數(shù)據(jù)信息,這就是Java中反射的基礎(chǔ)镊辕,為Java開發(fā)提供了很大的靈活性和通用性油够,是Java語言的一大亮點。

但是遇到泛型類(Generic Class)時征懈,Class就不靈光了石咬。在類型擦除(Type erase)后,Class丟失了泛型類型信息受裹。比如碌补,List<Integer>List<String>這兩個類型虏束,他們的Class對象是同一個棉饶,但是這兩個類型又截然不同,這兩個類型的變量不能相互賦值镇匀,也不能把一個List中的元素存入另一個List照藻。

其實從Java 1.5開始,Java提供了一個Type接口汗侵。Type接口是所有類型的公共父接口幸缕,包括類和接口(raw types)、參數(shù)化類型(parameterized types)晰韵、數(shù)組類型(array types)发乔、類型變量(type variables)和基本類型(primitive types),可以說雪猪,Type是Java語言中的頂級類型接口栏尚。Class就實現(xiàn)了Type接口,List<Integer>List<String>兩個類型的Type信息是不一樣的只恨。

有了Type類型译仗,Java開發(fā)可以更加靈活抬虽,可以寫出更加通用的代碼。

我們先看看Type接口的定義纵菌,它只有一個方法阐污,就是獲取Type的字符串名字。

public interface Type {

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

下面這些類和接口實現(xiàn)或擴展了Type接口:

graph BT

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

從上圖可以看到咱圆,除了Class實現(xiàn)了Type接口笛辟,還有四個接口(ParameterizedType、TypeVariable闷堡、GenericArrayType隘膘、WildcardType)擴展了Type接口。

1.1 Class類

這個大家已經(jīng)比較熟了杠览,就不多介紹了弯菊。它的getTypeName()返回的內(nèi)容就是Class的全限定域名(fully qualified domain name,F(xiàn)QDN)

1.2 ParameterizedType接口

ParameterizedType接口踱阿,跟它的名字一樣管钳,參數(shù)化類型,表示該類型帶有類型參數(shù)软舌,比如List<String>這種才漆。它的定義如下:

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

getActualTypeArguments()方法返回實際的參數(shù)類型,因為一個類型可以有多個類型參數(shù)佛点,所以是一個數(shù)組醇滥。比如List<String>類型的這個方法返回的是String類的Class對象(還記得Class實現(xiàn)了Type接口嗎?)

getRawType()方法返回擦除類型參數(shù)后的類型超营,比如List<String>類型的這個方法返回的是List類的Class對象

getOwnerType()方法鸳玩,如果當(dāng)前類型定義在另一個類型的內(nèi)部,則返回外部的類型演闭;否則不跟,則是一個頂層類型(top-level type),返回null米碰。比如List<String>類型的這個方法返回的是null窝革,但是Map.Entry<String, String>類型的這個方法返回的就是Map類的Class對象,因為Entry類定義在Map類的內(nèi)部

ParameterizedType接口可能是我們使用的最多的Type子接口吕座,畢竟我們使用Type類型虐译,往往是希望知道類型的泛型參數(shù)的具體化后的類型。當(dāng)然ParameterizedType的泛型參數(shù)不一定具體化了吴趴,它只是表明這個類型包含類型參數(shù)漆诽。泛型參數(shù)是否具體化,可以通過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接口

泛型數(shù)組類型接口,它的定義如下:

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

GenericArrayType接口只有一個getGenericComponentType()方法鼻忠,返回數(shù)組元素的類型涵但,如List<String>[]類型返回的結(jié)果是List<String>類型,他是一個ParameterizedType

1.5 WildcardType接口

表示泛型的通配符(?)類型帖蔓,比如List<?>中的?矮瘟。它的定義如下:

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

WildcardType接口提供了兩個接口,分別獲取通配符?的上下邊界塑娇。比如List<? extends Person>的上邊界是Person Class澈侠。List<? super Person>的下邊界是Person Class

2 獲取Type接口信息

我們可以通過類的全限定域名或類的對象獲取Class對象埋酬,Type則可以通過Class哨啃、FieldMethod写妥、Constructor拳球、Parameter這些表示類的元數(shù)據(jù)對象獲取,因為Field耳标、Method醇坝、Constructor邑跪、Parameter只能通過Class間接獲取次坡,所以Type信息都是從Class獲取的。

上述對象獲取Class信息的接口画畅,接口名前帶有generic前綴的往往就是獲取相應(yīng)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");

打印結(jié)果:

=====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");

打印結(jié)果:

=====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 獲取類型參數(shù)的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");

打印結(jié)果:

=====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");

打印結(jié)果:

=====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的例子應(yīng)該知道怎么回事了,后面不再給出例子轴踱,只給出獲取Type信息的方法

2.3.1 方法參數(shù)的Type: getGenericParameterTypes()

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

2.3.3 方法聲明的拋出異常的Type: getGenericExceptionTypes()

2.4 Constructor

2.3.1 構(gòu)造器參數(shù)的Type: getGenericParameterTypes()

2.3.2 構(gòu)造器聲明的拋出異常的Type: getGenericExceptionTypes()

2.5 Parameter

2.5.1 參數(shù)的Type: getParameterizedType()

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末症脂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诱篷,老刑警劉巖壶唤,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異棕所,居然都是意外死亡闸盔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門琳省,熙熙樓的掌柜王于貴愁眉苦臉地迎上來迎吵,“玉大人,你說我怎么就攤上這事针贬』鞣眩” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵桦他,是天一觀的道長蔫巩。 經(jīng)常有香客問我,道長快压,這世上最難降的妖魔是什么批幌? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮嗓节,結(jié)果婚禮上荧缘,老公的妹妹穿的比我還像新娘。我一直安慰自己拦宣,他們只是感情好截粗,可當(dāng)我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸵隧,像睡著了一般绸罗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上豆瘫,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天珊蟀,我揣著相機與錄音,去河邊找鬼外驱。 笑死育灸,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的昵宇。 我是一名探鬼主播磅崭,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瓦哎!你這毒婦竟也來了砸喻?” 一聲冷哼從身側(cè)響起柔逼,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎割岛,沒想到半個月后愉适,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡癣漆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年儡毕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扑媚。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡腰湾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出疆股,到底是詐尸還是另有隱情费坊,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布旬痹,位于F島的核電站附井,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏两残。R本人自食惡果不足惜永毅,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望人弓。 院中可真熱鬧沼死,春花似錦、人聲如沸崔赌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽健芭。三九已至县钥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間慈迈,已是汗流浹背若贮。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留痒留,地道東北人谴麦。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像狭瞎,于是被迫代替她去往敵國和親细移。 傳聞我的和親對象是個殘疾皇子搏予,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容