轻触开源(一)-Java泛型Type类型的应用和实践
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
轉(zhuǎn)載請(qǐng)注明出處:https://my.oschina.net/u/874727/blog/747427
Q:1025250620
在很多Java的開(kāi)源項(xiàng)目中都用到Java的泛型。比如Gson,就可以通過(guò)TypeToken<T>里的泛型參數(shù)來(lái)指定生成的類(lèi)型。鑒于網(wǎng)上關(guān)于泛型的文章并不多,為了非墨后面項(xiàng)目研究的需要,非墨開(kāi)始研究這部分的API。首先我們先來(lái)看一下在Java語(yǔ)言中泛型的例子:
public class MyTest<T1,T2 extends Number> {T1 member;public <T> void method(T m) {}}上述代碼中的標(biāo)志:T1,T2,T都是泛型類(lèi)型。Java的泛型檢查發(fā)生在編譯期,但是會(huì)在編譯后的JVM字節(jié)碼中增加類(lèi)型判斷的語(yǔ)句。為了方便大家理解這句話我們用一段代碼測(cè)試一下:
List<String> list = new ArrayList<>();try {Method m = list.getClass().getDeclaredMethod("add", new Class[]{Object.class});m.invoke(list, 1);m.invoke(list, 2);} catch (Exception e) {System.out.println("error");}在上述代碼中,雖然List變量指定了內(nèi)部元素的類(lèi)型String,但是在JVM運(yùn)行期間list對(duì)象的add方法調(diào)用的還是add(Ljava/lang/Object)簽名的方法。所以此段代碼執(zhí)行以后,控制臺(tái)不會(huì)有任何的輸出。基于此段代碼的基礎(chǔ)上,我們?cè)黾恿硪欢悟?yàn)證代碼:
for (Object o:list) {System.out.println(">>"+o);}//println >>1 >>2//will errorSystem.out.println("--->"+list.get(0));我們打印list中的子元素,如果子元素類(lèi)型是Object類(lèi)型的話代碼正常運(yùn)行。但是如果我們直接通過(guò)list.get的方式來(lái)獲取元素的時(shí)候程序就會(huì)拋出一個(gè)Class Cast異常。這是為什么呢?我們來(lái)看一下編譯后的JVM字節(jié)碼:
95: iconst_096: invokeinterface #56, 2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;101: checkcast #62; //class java/lang/String我們看到,Java編譯器在執(zhí)行段后插入了"checkcast"指令。也就是說(shuō)Java的泛型只不過(guò)是在執(zhí)行期間增加了一些類(lèi)型的檢查語(yǔ)句。但是,盡管泛型發(fā)生在編譯器,但是java還是把類(lèi)型記錄在類(lèi)型對(duì)象中。這就是我們今天討論的主角Type對(duì)象。我們看一下Type類(lèi)型的繼承樹(shù):
可以看出,Type類(lèi)型有四個(gè)直接接口子類(lèi),一個(gè)實(shí)現(xiàn)類(lèi)。此外還有另外一個(gè)接口GenericDeclaration。這個(gè)接口注明哪個(gè)類(lèi)可以聲明泛型。按照我們通過(guò)第一代碼塊可以知道,在Java語(yǔ)言中,可以聲明泛型的是類(lèi),方法。為什么沒(méi)有成員變量呢?我們?cè)诘谝粋€(gè)代碼塊中的"T1 member"不也是泛型聲明么?
或許這里我們應(yīng)該換一個(gè)說(shuō)法,對(duì)于member變量來(lái)說(shuō),它只是使用了泛型而并不是聲明了泛型,聲明T1泛型的是MyTest類(lèi)。我們把構(gòu)造器也看成方法的一部分,方法作為可執(zhí)行提,在方法和構(gòu)造器類(lèi)的基礎(chǔ)上JAVA又做了一層抽象--Executable類(lèi)。
Type類(lèi)型的四個(gè)直接子類(lèi)注釋里已經(jīng)給出了解釋。由于這些東西在javadoc中也說(shuō)的并不詳細(xì),也很難用辭藻把他描述的非常清楚。為了讓大家理解,非墨用一些簡(jiǎn)單的代碼來(lái)讓大家更加直觀的理解他們是個(gè)什么東東。我們先將可能涉及到的泛型類(lèi)型和聲明方式都寫(xiě)到一個(gè)類(lèi)中:
public static class TypeClazz<T1, T2 extends Number> {public T2 member;public T1 member2;public Collection<? extends Number> collection;public Collection<T2> collection2;public T2[] array;public <T extends Type> void method(T p1,T2 p2) {}}這個(gè)類(lèi)基本涵蓋了所有的泛型情況。我們還需要增加一些方法來(lái)打印我們所關(guān)心的信息:
public static Type printlnFieldType(String name) {System.out.println("name:"+name);Class clazzType = TypeClazz.class;Type type = null;try {Field field = clazzType.getDeclaredField(name);type = field.getGenericType();printlnType(field.getGenericType());} catch (Exception e) {} if (type instanceof ParameterizedType) {ParameterizedType ptype = (ParameterizedType)type;Type[] types = ptype.getActualTypeArguments();for (Type t:types) {System.out.print(">>");printlnType(t);}}return type;}public static void printlnMethodReturnType(String name) {System.out.println("name:"+name);Class clazzType = TypeClazz.class;try {Method[] ms = clazzType.getDeclaredMethods();Method method = null;for (Method m:ms) {if(m.getName().equals(name)) {method = m;break;}}printlnType(method.getGenericReturnType());} catch (Exception e) {e.printStackTrace();} }public static void printlnMethodParamTypes(String name) {System.out.println("name:"+name);Class clazzType = TypeClazz.class;try {Method[] ms = clazzType.getDeclaredMethods();Method method = null;for (Method m:ms) {if(m.getName().equals(name)) {method = m;break;}}Type[] types = method.getGenericParameterTypes();for (Type t:types) {printlnType(t);}} catch (Exception e) {e.printStackTrace();} }對(duì)于屬性,我們只關(guān)心它的類(lèi)型,而對(duì)于方法,我們不僅需要關(guān)心它的參數(shù)類(lèi)型,還要關(guān)心它的返回類(lèi)型。我們來(lái)調(diào)用一下以上的信息:
public static void main(String ...args) {printlnFieldType("member");printlnFieldType("member2");printlnFieldType("collection");printlnFieldType("collection2");printlnFieldType("array");printlnMethodReturnType("method");printlnMethodParamTypes("method");}控制臺(tái)輸出:
name:member >>>TypeVariable name:member2 >>>TypeVariable name:collection >>>ParameterizedType >>>>>WildcardType name:collection2 >>>ParameterizedType >>>>>TypeVariable name:array >>>GenericArrayType name:method >>>class name:method >>>TypeVariable >>>TypeVariable對(duì)于直接采用泛型方式定義的member來(lái)說(shuō),它都是TypeVariable類(lèi)型。而對(duì)于包含有泛型定義的collection來(lái)說(shuō),它屬于參數(shù)化的ParameterizedType類(lèi)型。由于數(shù)組類(lèi)型是單獨(dú)的類(lèi)型,并且由虛擬機(jī)動(dòng)態(tài)生成,因此,Type子類(lèi)中有專(zhuān)門(mén)針對(duì)數(shù)組的GenericArrayType類(lèi)型。例子中的array成員就是這種類(lèi)型。我們揭開(kāi)了collection的面紗后,泛型定義為"<? extends Number>"。這個(gè)就是通配符泛型WildcardType。
相信以上的例子已經(jīng)很能解釋這四種類(lèi)型的定義了,下一篇,非墨將帶著這些基礎(chǔ)知識(shí),深入到一個(gè)開(kāi)源項(xiàng)目的源碼中,看下別人的項(xiàng)目是如何巧妙的運(yùn)用這點(diǎn)的。
轉(zhuǎn)載于:https://my.oschina.net/u/874727/blog/747427
總結(jié)
以上是生活随笔為你收集整理的轻触开源(一)-Java泛型Type类型的应用和实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: BZOJ 1786 DP
- 下一篇: Java匿名内部类里为什么能用外部变量