摘要:對(duì)每個(gè)泛型類只生成唯一的一份目標(biāo)代碼該泛型類的所有實(shí)例都映射到這份目標(biāo)代碼上,在需要的時(shí)候執(zhí)行類型檢查和類型轉(zhuǎn)換。參考文章的模板是典型的實(shí)現(xiàn),而泛型則是實(shí)現(xiàn),將多種泛型類形實(shí)例映射到唯一的字節(jié)碼表示是通過類型擦除實(shí)現(xiàn)的。
一 前言:初識(shí)泛型
廢話不說,先來看一段代碼:
public class Holder { private Object data; public Holder(Object data ){ this.data = data; } public void setData(Object data) { this.data = data; } public Object getData() { return data; } public static void main(String[] args){ Holder holder = new Holder(new SomeNode()); SomeNode someNode = holder.getData(); } } class SomeNode{}
Holder類是一個(gè)容器,它的作用是用來保存其他類的,這里我們用它來保存SomeNode類,隨后把它取出來,編譯運(yùn)行,結(jié)果如下:
Error:(21, 43) java: incompatible types required: SomeNode found: java.lang.Object
意思是,需要的是SomeNode,取出來的卻是Object,如此看來,如果我想保存SomeNode類,就只能把data聲明為SomeNode:
private SomeNode data;
這就意味著我們需要為每一個(gè)類創(chuàng)造一個(gè)Holder,這肯定是不行的,于是泛型的作用來了,泛型,可以理解為任何類型,意思是我可以聲明一個(gè)可以容納任何類型的容器:
public class Holder{ private T data; public Holder(T data ){ this.data = data; } public void setData(T data) { this.data = data; } public T getData() { return data; } public static void main(String[] args){ Holder holder = new Holder (new SomeNode()); SomeNode someNode = holder.getData(); } } class SomeNode{}
注意寫法,在類聲明后面加個(gè)就行了,你也可以加,只是一個(gè)占位符,形參而已。然后我們?cè)侔阉〕鰜恚?/p>
Process finished with exit code 0
程序沒有報(bào)錯(cuò),如果這時(shí)候我們使用holder的set()方法去插入設(shè)置一些非SomeNode類型的值,代碼如下:
public static void main(String[] args){ Holderholder = new Holder (new SomeNode()); SomeNode someNode = holder.getData(); holder.setData("AAAA"); }
看結(jié)果:
Error:(22, 15) java: method setData in class Holdercannot be applied to given types; required: SomeNode found: java.lang.String reason: actual argument java.lang.String cannot be converted to SomeNode by method invocation conversion
泛型機(jī)制就自動(dòng)為我們報(bào)錯(cuò),很方便。
二 泛型類:元組(Tuple),返回多個(gè)對(duì)象熟悉python的同學(xué)都知道元組的概念,它是一個(gè)只讀列表,在返回多個(gè)結(jié)果時(shí)是很有用的,我們利用泛型特性來創(chuàng)造一個(gè)包含兩個(gè)對(duì)象的元組:
public class Tuple { public static void main(String[] args){ TwoTuplet = new TwoTuple ("Monkey",12); System.out.println(t.toString()); } } class TwoTuple{ final A first; final B second; public TwoTuple(A a,B b){ first = a; second = b; } public String toString(){ return "("+first+","+second+")"; } }
來看結(jié)果:
(Monkey,12)
是不是很方便:)如果想要一個(gè)長(zhǎng)度為3的元組可以這么寫:
public class Tuple { public static void main(String[] args){ ThreeTuplet = new ThreeTuple ("Dog",12,true); System.out.println(t.toString()); } } class TwoTuple{ final A first; final B second; public TwoTuple(A a,B b){ first = a; second = b; } public String toString(){ return "("+first+","+second+")"; } } class ThreeTuple extends TwoTuple{ final C three; public ThreeTuple(A a,B b,C c){ super(a,b); three = c; } public String toString(){ return "("+first+","+second+","+three+")"; } }
結(jié)果如下:
(Dog,12,true)三 泛型接口
泛型接口的定義和泛型類的定義類似,我們來定義一個(gè)生成器接口:
public interface Generator{ T next(); }
接著我們實(shí)現(xiàn)這個(gè)接口,來生成斐波拉契數(shù):
public class Fib implements Generator四 泛型方法{ private int count = 0; @Override public Integer next() { return fib(count++); } private int fib(int n){ if (n<2) return 1; else return fib(n-2) + fib(n-1); } public static void main(String[] args){ Fib f = new Fib(); for (int i=0;i<100;i++){ System.out.println(f.next()); } } }
比起泛型類,我們更推薦去使用泛型方法,泛型方法定義起來也很簡(jiǎn)單,我們只需將泛型參數(shù)放在返回類型前面即可:
public class GenericMethods { publicvoid f(T x){ System.out.println(x.getClass().getName()); } public static void main(String[] args){ GenericMethods g = new GenericMethods(); g.f("Hello"); g.f(100); g.f(true); } }
這里我們定義了一個(gè)泛型方法f(),并使用getClass獲取類的相關(guān)信息(關(guān)于Class對(duì)象的知識(shí)點(diǎn)這里),來看結(jié)果:
java.lang.String java.lang.Integer java.lang.Boolean
這里還要注意一下Varargs(變長(zhǎng)參數(shù))機(jī)制和泛型的結(jié)合:
public class GenericVarargs { public staticList makeList(T...args){ List list = new ArrayList (); for (T item : args){ list.add(item); } return list; } public static void main(String[] args){ List list = makeList("A","B","C","D"); System.out.println(list); } }
結(jié)果如下:
[A, B, C, D]六 類型擦除
在認(rèn)識(shí)類型擦除之前,我們首先要明白編譯器對(duì)泛型的處理有兩種方式:
1.Code specialization
在實(shí)例化一個(gè)泛型類或者泛型方法是都生成一份新的字節(jié)碼,比如對(duì)于List
2.Code sharing
對(duì)每個(gè)泛型類只生成唯一的一份目標(biāo)代碼;該泛型類的所有實(shí)例都映射到這份目標(biāo)代碼上,在需要的時(shí)候執(zhí)行類型檢查和類型轉(zhuǎn)換。參考文章
C++的模板是典型的Code specialization實(shí)現(xiàn),而Java泛型則是Code sharing實(shí)現(xiàn),將多種泛型類形實(shí)例映射到唯一的字節(jié)碼表示是通過類型擦除(type erasue)實(shí)現(xiàn)的。對(duì)擦除更通俗的理解就是:編譯器生成的bytecode是不包涵泛型信息的。我們看下面的代碼:
public class ErasedType { public static void main(String[] args){ Class c1 = new ArrayList().getClass(); Class c2 = new ArrayList ().getClass(); System.out.println(c1 == c2); } }
結(jié)果如下:
true
也就是說我們?cè)趯?shí)例化ArrayList
public class ErasedType { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Listlist = new ArrayList (); list.add("ABC"); list.getClass().getMethod("add",Object.class).invoke(list,123); System.out.println(list); } }
看結(jié)果:
[ABC, 123]
我們很順利的把Integer型的123插入到了String的List里:)
七 后記由于類型擦除的存在,我們往往會(huì)在使用泛型特性的時(shí)候遇到一些詭異的問題,由于篇幅原因,這里不展開了:)我將在另外一篇文章中集中的總結(jié)一下這方面的問題。
我的微信號(hào)是aristark,歡迎交流指正!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/65687.html
摘要:總結(jié)數(shù)組與泛型的關(guān)系還是有點(diǎn)復(fù)雜的,中不允許直接創(chuàng)建泛型數(shù)組。本文分析了其中原因并且總結(jié)了一些創(chuàng)建泛型數(shù)組的方式。 簡(jiǎn)介 上一篇文章介紹了泛型的基本用法以及類型擦除的問題,現(xiàn)在來看看泛型和數(shù)組的關(guān)系。數(shù)組相比于Java 類庫(kù)中的容器類是比較特殊的,主要體現(xiàn)在三個(gè)方面: 數(shù)組創(chuàng)建后大小便固定,但效率更高 數(shù)組能追蹤它內(nèi)部保存的元素的具體類型,插入的元素類型會(huì)在編譯期得到檢查 數(shù)組可以持...
摘要:操作對(duì)應(yīng)字節(jié)碼中的個(gè)字節(jié)我們可以看到最關(guān)鍵的操作其實(shí)就是調(diào)用的其實(shí)是類的方法,此方法的入?yún)㈩愋褪牵祷刂殿愋褪牵g過來就是類的方法,執(zhí)行完后將獲得的結(jié)果做了,檢查返回的對(duì)象類型是否是。 語(yǔ)法糖(Syntactic Sugar)的出現(xiàn)是為了降低我們編寫某些代碼時(shí)陷入的重復(fù)或繁瑣,這使得我們使用語(yǔ)法糖后可以寫出簡(jiǎn)明而優(yōu)雅的代碼。在Java中不加工的語(yǔ)法糖代碼運(yùn)行時(shí)可不會(huì)被虛擬機(jī)接受,因此...
摘要:然而中的泛型使用了類型擦除,所以只是偽泛型。總結(jié)本文介紹了泛型的使用,以及類型擦除相關(guān)的問題。一般情況下泛型的使用比較簡(jiǎn)單,但是某些情況下,尤其是自己編寫使用泛型的類或者方法時(shí)要注意類型擦除的問題。 簡(jiǎn)介 Java 在 1.5 引入了泛型機(jī)制,泛型本質(zhì)是參數(shù)化類型,也就是說變量的類型是一個(gè)參數(shù),在使用時(shí)再指定為具體類型。泛型可以用于類、接口、方法,通過使用泛型可以使代碼更簡(jiǎn)單、安全。然...
摘要:總結(jié)泛型的類型必須是引用類型,不能是基本類型,泛型的個(gè)數(shù)可以有多個(gè),可以使用對(duì)創(chuàng)建對(duì)象時(shí)的泛型類型以及方法參數(shù)類型進(jìn)行限制,如使用關(guān)鍵字和對(duì)泛型的具體類型進(jìn)行向下限制或向上限制,最后一點(diǎn),可以聲明泛型數(shù)組,但是不能創(chuàng)建泛型數(shù)組的實(shí)例。 自從 JDK 1.5 提供了泛型概念,泛型使得開發(fā)者可以定義較為安全的類型,不至于強(qiáng)制類型轉(zhuǎn)化時(shí)出現(xiàn)類型轉(zhuǎn)化異常,在沒有反省之前,可以通過 Object...
閱讀 2096·2021-11-23 09:51
閱讀 2839·2021-11-22 15:35
閱讀 2937·2019-08-30 15:53
閱讀 1037·2019-08-30 14:04
閱讀 3276·2019-08-29 12:39
閱讀 1802·2019-08-28 17:57
閱讀 1086·2019-08-26 13:39
閱讀 551·2019-08-26 13:34