轻触开源(一)-Java泛型Type类型的应用和实践
2019獨角獸企業重金招聘Python工程師標準>>>
轉載請注明出處:https://my.oschina.net/u/874727/blog/747427
Q:1025250620
在很多Java的開源項目中都用到Java的泛型。比如Gson,就可以通過TypeToken<T>里的泛型參數來指定生成的類型。鑒于網上關于泛型的文章并不多,為了非墨后面項目研究的需要,非墨開始研究這部分的API。首先我們先來看一下在Java語言中泛型的例子:
public class MyTest<T1,T2 extends Number> {T1 member;public <T> void method(T m) {}}上述代碼中的標志:T1,T2,T都是泛型類型。Java的泛型檢查發生在編譯期,但是會在編譯后的JVM字節碼中增加類型判斷的語句。為了方便大家理解這句話我們用一段代碼測試一下:
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變量指定了內部元素的類型String,但是在JVM運行期間list對象的add方法調用的還是add(Ljava/lang/Object)簽名的方法。所以此段代碼執行以后,控制臺不會有任何的輸出。基于此段代碼的基礎上,我們增加另一段驗證代碼:
for (Object o:list) {System.out.println(">>"+o);}//println >>1 >>2//will errorSystem.out.println("--->"+list.get(0));我們打印list中的子元素,如果子元素類型是Object類型的話代碼正常運行。但是如果我們直接通過list.get的方式來獲取元素的時候程序就會拋出一個Class Cast異常。這是為什么呢?我們來看一下編譯后的JVM字節碼:
95: iconst_096: invokeinterface #56, 2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;101: checkcast #62; //class java/lang/String我們看到,Java編譯器在執行段后插入了"checkcast"指令。也就是說Java的泛型只不過是在執行期間增加了一些類型的檢查語句。但是,盡管泛型發生在編譯器,但是java還是把類型記錄在類型對象中。這就是我們今天討論的主角Type對象。我們看一下Type類型的繼承樹:
可以看出,Type類型有四個直接接口子類,一個實現類。此外還有另外一個接口GenericDeclaration。這個接口注明哪個類可以聲明泛型。按照我們通過第一代碼塊可以知道,在Java語言中,可以聲明泛型的是類,方法。為什么沒有成員變量呢?我們在第一個代碼塊中的"T1 member"不也是泛型聲明么?
或許這里我們應該換一個說法,對于member變量來說,它只是使用了泛型而并不是聲明了泛型,聲明T1泛型的是MyTest類。我們把構造器也看成方法的一部分,方法作為可執行提,在方法和構造器類的基礎上JAVA又做了一層抽象--Executable類。
Type類型的四個直接子類注釋里已經給出了解釋。由于這些東西在javadoc中也說的并不詳細,也很難用辭藻把他描述的非常清楚。為了讓大家理解,非墨用一些簡單的代碼來讓大家更加直觀的理解他們是個什么東東。我們先將可能涉及到的泛型類型和聲明方式都寫到一個類中:
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) {}}這個類基本涵蓋了所有的泛型情況。我們還需要增加一些方法來打印我們所關心的信息:
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();} }對于屬性,我們只關心它的類型,而對于方法,我們不僅需要關心它的參數類型,還要關心它的返回類型。我們來調用一下以上的信息:
public static void main(String ...args) {printlnFieldType("member");printlnFieldType("member2");printlnFieldType("collection");printlnFieldType("collection2");printlnFieldType("array");printlnMethodReturnType("method");printlnMethodParamTypes("method");}控制臺輸出:
name:member >>>TypeVariable name:member2 >>>TypeVariable name:collection >>>ParameterizedType >>>>>WildcardType name:collection2 >>>ParameterizedType >>>>>TypeVariable name:array >>>GenericArrayType name:method >>>class name:method >>>TypeVariable >>>TypeVariable對于直接采用泛型方式定義的member來說,它都是TypeVariable類型。而對于包含有泛型定義的collection來說,它屬于參數化的ParameterizedType類型。由于數組類型是單獨的類型,并且由虛擬機動態生成,因此,Type子類中有專門針對數組的GenericArrayType類型。例子中的array成員就是這種類型。我們揭開了collection的面紗后,泛型定義為"<? extends Number>"。這個就是通配符泛型WildcardType。
相信以上的例子已經很能解釋這四種類型的定義了,下一篇,非墨將帶著這些基礎知識,深入到一個開源項目的源碼中,看下別人的項目是如何巧妙的運用這點的。
轉載于:https://my.oschina.net/u/874727/blog/747427
總結
以上是生活随笔為你收集整理的轻触开源(一)-Java泛型Type类型的应用和实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BZOJ 1786 DP
- 下一篇: 《测试驱动数据库开发》—第1章1.2节谁