新聞中心
一直對(duì)jdk的ref使用比較模糊,早上花了點(diǎn)時(shí)間簡(jiǎn)單的整理了下,也幫助自己理解一下泛型的一些處理。

高臺(tái)網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)公司!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、響應(yīng)式網(wǎng)站設(shè)計(jì)等網(wǎng)站項(xiàng)目制作,到程序開(kāi)發(fā),運(yùn)營(yíng)維護(hù)。創(chuàng)新互聯(lián)公司公司2013年成立到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專(zhuān)注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)公司。
java中class,method,field的繼承體系
java中所有對(duì)象的類(lèi)型定義類(lèi)Type
說(shuō)明:
Type : Type is the common superinterface for all types in the Java programming language. These include raw types, parameterized types, array types, type variables and primitive types.
使用
一般我們不直接操作Type類(lèi)型,所以第一次使用會(huì)對(duì)這個(gè)比較陌生,相對(duì)內(nèi)部的一些概念。
根據(jù)Type類(lèi)型分類(lèi),整理了一個(gè)type -> class的轉(zhuǎn)換過(guò)程,同理也包括處理Generic Type。支持多級(jí)泛型處理。
Java代碼
- private static Class getClass(Type type, int i) {
- if (type instanceof ParameterizedType) { // 處理泛型類(lèi)型
- return getGenericClass((ParameterizedType) type, i);
- } else if (type instanceof TypeVariable) {
- return (Class ) getClass(((TypeVariable) type).getBounds()[0], 0); // 處理泛型擦拭對(duì)象
- } else {// class本身也是type,強(qiáng)制轉(zhuǎn)型
- return (Class ) type;
- }
- }
- private static Class getGenericClass(ParameterizedType parameterizedType, int i) {
- Object genericClass = parameterizedType.getActualTypeArguments()[i];
- if (genericClass instanceof ParameterizedType) { // 處理多級(jí)泛型
- return (Class ) ((ParameterizedType) genericClass).getRawType();
- } else if (genericClass instanceof GenericArrayType) { // 處理數(shù)組泛型
- return (Class ) ((GenericArrayType) genericClass).getGenericComponentType();
- } else if (genericClass instanceof TypeVariable) { // 處理泛型擦拭對(duì)象
- return (Class ) getClass(((TypeVariable) genericClass).getBounds()[0], 0);
- } else {
- return (Class ) genericClass;
- }
- }
測(cè)試代碼:
Java代碼
- interface GeneircInteface {
- T method1(T obj);
- }
- interface CommonInteface {
- Integer method2(Integer obj);
- }
- class BaseGeneircInteface implements GeneircInteface {
- protected R result;
- @Override
- public R method1(R obj) {
- return obj;
- }
- }
- class GenericClass extends BaseGeneircInteface > implements GeneircInteface >, CommonInteface {
- @Override
- public List method1(List obj) {
- result = obj;
- return result;
- }
- public Integer method2(Integer obj) {
- return obj;
- }
- public extends Throwable> T method3(T obj) throws E {
- return obj;
- }
- }
針對(duì)class的泛型接口使用:
Java代碼
- private static void classGeneric() {
- System.out.println("\n--------------------- classGeneric ---------------------");
- GenericClass gc = new GenericClass();
- Type[] gis = gc.getClass().getGenericInterfaces(); // 接口的泛型信息
- Type gps = gc.getClass().getGenericSuperclass(); // 父類(lèi)的泛型信息
- TypeVariable [] gtr = gc.getClass().getTypeParameters(); // 當(dāng)前接口的參數(shù)信息
- System.out.println("============== getGenericInterfaces");
- for (Type t : gis) {
- System.out.println(t + " : " + getClass(t, 0));
- }
- System.out.println("============== getGenericSuperclass");
- System.out.println(getClass(gps, 0));
- System.out.println("============== getTypeParameters");
- for (TypeVariable t : gtr) {
- StringBuilder stb = new StringBuilder();
- for (Type tp : t.getBounds()) {
- stb.append(tp + " : ");
- }
- System.out.println(t + " : " + t.getName() + " : " + stb);
- }
- }
針對(duì)method的泛型接口使用:
Java代碼
- private static void methodGeneric() throws Exception {
- System.out.println("\n--------------------- methodGeneric ---------------------");
- GenericClass gc = new GenericClass();
- Method method3 = gc.getClass().getDeclaredMethod("method3", new Class[] { Object.class });
- Type[] gpt3 = method3.getGenericParameterTypes();
- Type[] get3 = method3.getGenericExceptionTypes();
- Type gt3 = method3.getGenericReturnType();
- System.out.println("============== getGenericParameterTypes");
- for (Type t : gpt3) {
- System.out.println(t + " : " + getClass(t, 0));
- }
- System.out.println("============== getGenericExceptionTypes");
- for (Type t : get3) {
- System.out.println(t + " : " + getClass(t, 0));
- }
- System.out.println("============== getType");
- System.out.println(gt3 + " : " + getClass(gt3, 0));
- }
針對(duì)field的泛型接口使用:
Java代碼
- private static void fieldGeneric() throws Exception {
- System.out.println("\n--------------------- fieldGeneric ---------------------");
- GenericClass gc = new GenericClass();
- Field field = gc.getClass().getSuperclass().getDeclaredField("result");
- Type gt = field.getGenericType();
- Type ft = field.getType();
- System.out.println("============== getGenericType");
- System.out.println(gt + " : " + getClass(gt, 0));
- System.out.println("============== getType");
- System.out.println(ft + " : " + getClass(ft, 0));
- }
輸出結(jié)果:
Java代碼
- --------------------- classGeneric ---------------------
- ============== getGenericInterfaces
- com.agapple.misc.GeneircInteface > : interface java.util.List
- interface com.agapple.misc.CommonInteface : interface com.agapple.misc.CommonInteface
- ============== getGenericSuperclass
- interface java.util.List
- ============== getTypeParameters
- --------------------- fieldGeneric ---------------------
- ============== getGenericType
- R : class java.lang.Object
- ============== getType
- class java.lang.Object : class java.lang.Object
- --------------------- methodGeneric ---------------------
- ============== getGenericParameterTypes
- T : class java.lang.Object
- ============== getGenericExceptionTypes
- E : class java.lang.Throwable
- ============== getType
- T : class java.lang.Object
結(jié)果說(shuō)明:
因?yàn)榉盒偷牟潦?,?duì)應(yīng)的GeneircInteface和BaseGeneircInteface,在源碼信息已被擦除對(duì)應(yīng)的類(lèi)型,進(jìn)行了upper轉(zhuǎn)型,所以取到的是Object??梢允褂胑xtends
GenericClass在類(lèi)定義時(shí),聲明了繼承父接口的泛型為L(zhǎng)ist,所以再通過(guò)接口和父類(lèi)獲取泛型信息時(shí),是能正確的獲取。通過(guò)javap -v可以獲取對(duì)應(yīng)的class信息
Java代碼
- const #46 = Asciz Lcom/agapple/misc/BaseGeneircInteface ;>;Lcom/agapple/misc/GeneircInteface ;>;Lcom/agapple/misc/CommonInteface;;
而在GenericClass中定義的方法method3,在class信息是一個(gè)被向上轉(zhuǎn)型后擦拭的信息。所以獲取method3的相關(guān)泛型信息是沒(méi)有的。
Java代碼
- method3;
- const #36 = Asciz (Ljava/lang/Object;)Ljava/lang/Object;;
- const #37 = Asciz Exceptions;
- const #38 = class #39; // java/lang/Throwable
- const #39 = Asciz java/lang/Throwable;
- const #40 = Asciz (TT;)TT;^TE;;
- const #41 = Asciz TT;;
思考問(wèn)題:
List list = new ArrayList(); 是否有獲取對(duì)應(yīng)的String泛型信息? 不能,臨時(shí)變量不能保存泛型信息到具體class對(duì)象中,List和List對(duì)應(yīng)的class實(shí)體是同一個(gè)。
Java代碼
- GeneircInteface gi = new GeneircInteface () {
- @Override
- public Integer method1(Integer obj) {
- return 1;
- }
- };
通過(guò)匿名類(lèi)的方式,是否可以獲取Integer的泛型信息? 能,匿名類(lèi)也會(huì)在進(jìn)行class compiler保存泛型信息。
假如本文例子中的method3,是放在父類(lèi)中BaseGeneircInteface中進(jìn)行申明,GenericClass中指定R為L(zhǎng)ist,是否可以獲取到對(duì)應(yīng)的泛型信息? 不能,理由和問(wèn)題1類(lèi)似。
備注
具體泛型擦拭和信息保存,引用了撒迦的一段回復(fù),解釋的挺詳盡了。
RednaxelaFX 寫(xiě)道
Java泛型有這么一種規(guī)律:
位于聲明一側(cè)的,源碼里寫(xiě)了什么到運(yùn)行時(shí)就能看到什么;
位于使用一側(cè)的,源碼里寫(xiě)什么到運(yùn)行時(shí)都沒(méi)了。
什么意思呢?“聲明一側(cè)”包括泛型類(lèi)型(泛型類(lèi)與泛型接口)聲明、帶有泛型參數(shù)的方法和域的聲明。注意局部變量的聲明不算在內(nèi),那個(gè)屬于“使用”一側(cè)。
Java代碼
- import java.util.List;
- import java.util.Map;
- public class GenericClass { // 1
- private List list; // 2
- private Map map; // 3
- public U genericMethod(Map m) { // 4
- return null;
- }
- }
上面代碼里,帶有注釋的行里的泛型信息在運(yùn)行時(shí)都還能獲取到,原則是源碼里寫(xiě)了什么運(yùn)行時(shí)就能得到什么。針對(duì)1的GenericClass,運(yùn)行時(shí)通過(guò)Class.getTypeParameters()方法得到的數(shù)組可以獲取那個(gè)“T”;同理,2的T、3的java.lang.String與T、4的T與U都可以獲得。
這是因?yàn)閺腏ava 5開(kāi)始class文件的格式有了調(diào)整,規(guī)定這些泛型信息要寫(xiě)到class文件中。以上面的map為例,通過(guò)javap來(lái)看它的元數(shù)據(jù)可以看到記錄了這樣的信息:
Javap代碼
- private java.util.Map map;
- Signature: Ljava/util/Map;
- Signature: length = 0x2
- 00 0A
乍一看,private java.util.Map map;不正好顯示了它的泛型類(lèi)型被擦除了么?
但仔細(xì)看會(huì)發(fā)現(xiàn)有兩個(gè)Signature,下面的一個(gè)有兩字節(jié)的數(shù)據(jù),0x0A。到常量池找到0x0A對(duì)應(yīng)的項(xiàng),是:
Javap代碼
- const #10 = Asciz Ljava/util/Map ;;
也就是內(nèi)容為“Ljava/util/Map;”的一個(gè)字符串。
根據(jù)Java 5開(kāi)始的新class文件格式規(guī)范,方法與域的描述符增添了對(duì)泛型信息的記錄,用一對(duì)尖括號(hào)包圍泛型參數(shù),其中普通的引用類(lèi)型用“La/b/c/D;”的格式記錄,未綁定值的泛型變量用“Txxx;”的格式記錄,其中xxx就是源碼中聲明的泛型變量名。類(lèi)型聲明的泛型信息也以類(lèi)似下面的方式記了下來(lái):
Javap代碼
- public class GenericClass extends java.lang.Object
- Signature: length = 0x2
- 00 12
- // ...
- const #18 = Asciz Ljava/lang/Object;;
詳細(xì)信息請(qǐng)參考官方文檔:http://java.sun.com/docs/books/jvms/second_edition/ClassFileFormat-Java5.pdf
相比之下,“使用一側(cè)”的泛型信息則完全沒(méi)有被保留下來(lái),在Java源碼編譯到class文件后就確實(shí)丟失了。也就是說(shuō),在方法體內(nèi)的泛型局部變量、泛型方法調(diào)用之類(lèi)的泛型信息編譯后都消失了。
Java代碼
- import java.util.ArrayList;
- import java.util.List;
- public class TestClass {
- public static void main(String[] args) {
- List list = null; // 1
- list = new ArrayList (); // 2
- for (int i = 0; i < 10; i++) ;
- }
- }
上面代碼中,1留下的痕跡是:main()方法的StackMapTable屬性里可以看到:
Java代碼
- StackMapTable: number_of_entries = 2
- frame_type = 253 /* append */
- offset_delta = 12
- locals = [ class java/util/List, int ]
- frame_type = 250 /* chop */
- offset_delta = 11
但這里是沒(méi)有留下泛型信息的。這段代碼只所以寫(xiě)了個(gè)空的for循環(huán)就是為了迫使javac生成那個(gè)StackMapTable,讓1多留個(gè)影。
如果main()里用到了list的方法,那么那些方法調(diào)用點(diǎn)上也會(huì)留下1的痕跡,例如如果調(diào)用list.add("");,則會(huì)留下“java/util/List.add:(Ljava/lang/Object;)Z”這種記錄。
2留下的是“java/util/ArrayList."":()V”,同樣也丟失了泛型信息。
由上述討論可知,想對(duì)帶有未綁定的泛型變量的泛型類(lèi)型獲取其實(shí)際類(lèi)型是不現(xiàn)實(shí)的,因?yàn)閏lass文件里根本沒(méi)記錄實(shí)際類(lèi)型的信息。覺(jué)得這句話太拗口的話用例子來(lái)理解:要想對(duì)java.util.List獲取E的實(shí)際類(lèi)型是不現(xiàn)實(shí)的,因?yàn)長(zhǎng)ist.class文件里只記錄了E,卻沒(méi)記錄使用List時(shí)E的實(shí)際類(lèi)型。
想對(duì)局部變量等“使用一側(cè)”的已綁定的泛型類(lèi)型獲取其實(shí)際類(lèi)型也不現(xiàn)實(shí),同樣是因?yàn)閏lass文件中根本沒(méi)記錄這個(gè)信息。例子直接看上面講“使用一側(cè)”的就可以了。
知道了什么信息有記錄,什么信息沒(méi)有記錄之后,也就可以省點(diǎn)力氣不去糾結(jié)“拿不到T的實(shí)際類(lèi)型”、“建不出T類(lèi)型的數(shù)組”之類(lèi)的問(wèn)題了orz
當(dāng)前文章:詳解Java泛型type體系整理
標(biāo)題URL:http://fisionsoft.com.cn/article/codgepo.html


咨詢(xún)
建站咨詢(xún)
