日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

编译期间确定类型安全——泛型(Generics)

發(fā)布時(shí)間:2024/4/15 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 编译期间确定类型安全——泛型(Generics) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

  泛型是提供給Javac編譯器使用的。可以限定集合中輸入的類(lèi)型,讓編譯器擋住原始程序的非法輸入,編譯器編譯帶類(lèi)型說(shuō)明的集合時(shí)會(huì)去掉“類(lèi)型”信息,使程序運(yùn)行效率不受影響,對(duì)于參數(shù)化的泛型類(lèi)型,getClass()方法的返回值和原始類(lèi)型完全一樣,由于編譯生成的字節(jié)碼會(huì)去掉泛型的類(lèi)型信息,只要能跳過(guò)編譯器,就可以往某個(gè)泛型集合中加入其它類(lèi)型的數(shù)據(jù),例如,用反射得到集合,再調(diào)用其add方法即可。

  ArrayList<E>類(lèi)定義和ArrayList<Integer>類(lèi)引用中涉及如下術(shù)語(yǔ):

  整個(gè)稱(chēng)為ArrayList<E>泛型類(lèi)型,ArrayList<E>中的E稱(chēng)為類(lèi)型變量或類(lèi)型參數(shù),整個(gè)ArrayList<Integer>稱(chēng)為參數(shù)化的類(lèi)型,ArrayList<Integer>中的Integer稱(chēng)為類(lèi)型參數(shù)的實(shí)例或?qū)嶋H類(lèi)型參數(shù),ArrayList<Integer>中的<>念著typeof,ArrayList稱(chēng)為原始類(lèi)型。

  參數(shù)化類(lèi)型與原始類(lèi)型的兼容性:
  參數(shù)化類(lèi)型可以引用一個(gè)原始類(lèi)型的對(duì)象,編譯報(bào)告警告,例如,  

  Collection<String> c = new Vector ();

  原始類(lèi)型可以引用一個(gè)參數(shù)化類(lèi)型的對(duì)象,編譯報(bào)告警告,例如,

  Collection c = new Vector<String>();

  參數(shù)化類(lèi)型不考慮類(lèi)型參數(shù)的繼承關(guān)系:

  Vector<String> v = new Vector<Object>() // 錯(cuò)誤Vector<Object> v = new Vector<String>() // 也錯(cuò)誤

  類(lèi)型擦除

  正確理解泛型概念的首要前提是理解類(lèi)型擦除(type erasure)。 Java中的泛型類(lèi)似于C++中的模板,但是這種相似性?xún)H限于表面,Java中的泛型基本上都是在編譯器這個(gè)層次來(lái)實(shí)現(xiàn)的。屬于編譯器執(zhí)行類(lèi)型檢查和類(lèi)型診斷,然后生成普通的非泛型的字節(jié)碼,也就是在生成的Java字節(jié)代碼中是不包含泛型中的類(lèi)型信息的,使用泛型的時(shí)候加上的類(lèi)型參數(shù),會(huì)被編譯器在編譯的時(shí)候去掉。這種實(shí)現(xiàn)技術(shù)稱(chēng)為類(lèi)型擦除。如在代碼中定義的List<Object>和List<String>等類(lèi)型,在編譯之后都會(huì)變成List。JVM看到的只是List,而由泛型附加的類(lèi)型信息對(duì)JVM來(lái)說(shuō)是不可見(jiàn)的。Java編譯器會(huì)在編譯時(shí)盡可能的發(fā)現(xiàn)可能出錯(cuò)的地方,但是仍然無(wú)法避免在運(yùn)行時(shí)刻出現(xiàn)類(lèi)型轉(zhuǎn)換異常的情況。

  很多泛型的奇怪特性都與這個(gè)類(lèi)型擦除的存在有關(guān),包括:

  • 泛型類(lèi)并沒(méi)有自己獨(dú)有的Class類(lèi)對(duì)象。比如并不存在List<String>.class或是List<Integer>.class,而只有List.class;
  • 靜態(tài)變量是被泛型類(lèi)的所有實(shí)例所共享的。對(duì)于聲明為MyClass<T>的類(lèi),訪(fǎng)問(wèn)其中的靜態(tài)變量的方法仍然是 MyClass.myStaticVar。不管是通過(guò)new MyClass<String>還是new MyClass<Integer>創(chuàng)建的對(duì)象,都是共享一個(gè)靜態(tài)變量。
  • 泛型的類(lèi)型參數(shù)不能用在Java異常處理的catch語(yǔ)句中。因?yàn)楫惓L幚硎怯蒍VM在運(yùn)行時(shí)刻來(lái)進(jìn)行的。由于類(lèi)型信息被擦除,JVM是無(wú)法區(qū)分兩個(gè)異常類(lèi)型MyException<String>和MyException<Integer>的。對(duì)于JVM來(lái)說(shuō),它們都是MyException類(lèi)型的。也就無(wú)法執(zhí)行與異常對(duì)應(yīng)的catch語(yǔ)句。

  實(shí)例分析

  了解了類(lèi)型擦除機(jī)制之后,就會(huì)明白編譯器承擔(dān)了全部的類(lèi)型檢查工作。編譯器禁止某些泛型的使用方式,正是為了確保類(lèi)型的安全性。以上面提到的List<Object>和List<String>為例來(lái)具體分析:

  public void inspect(List<Object> list) { for (Object obj : list) { System.out.println(obj); } list.add(1); //這個(gè)操作在當(dāng)前方法的上下文是合法的。 }public void test() { List<String> strs = new ArrayList<String>(); inspect(strs); //編譯錯(cuò)誤 }

  這段代碼中,inspect()方法接受List<Object>作為參數(shù),當(dāng)在test方法中試圖傳入List<String>的時(shí)候,會(huì)出現(xiàn)編譯錯(cuò)誤。假設(shè)這樣的做法是允許的,那么在inspect方法就可以通過(guò)list.add(1)來(lái)向集合中添加一個(gè)數(shù)字。這樣在test方法看來(lái),其聲明為L(zhǎng)ist<String>的集合中卻被添加了一個(gè)Integer類(lèi)型的對(duì)象。這顯然是違反類(lèi)型安全的原則的,在某個(gè)時(shí)候肯定會(huì)拋出ClassCastException。因此,編譯器禁止這樣的行為。編譯器會(huì)盡可能的檢查可能存在的類(lèi)型安全問(wèn)題。對(duì)于確定是違反相關(guān)原則的地方,會(huì)給出編譯錯(cuò)誤。當(dāng)編譯器無(wú)法判斷類(lèi)型的使用是否正確的時(shí)候,會(huì)給出警告信息。?  

  通配符與上下界

  在使用泛型類(lèi)的時(shí)候,既可以指定一個(gè)具體的類(lèi)型,如List<String>就聲明了具體的類(lèi)型是String;也可以用通配符?來(lái)表示未知類(lèi)型,如List<?>就聲明了List中包含的元素類(lèi)型是未知的。 通配符所代表的其實(shí)是一組類(lèi)型,但具體的類(lèi)型是未知的。List<?>所聲明的就是所有類(lèi)型都是可以的。但是List<?>并不等同于List<Object>。List<Object>實(shí)際上確定了List中包含的是Object及其子類(lèi),在使用的時(shí)候都可以通過(guò)Object來(lái)進(jìn)行引用。而List<?>則其中所包含的元素類(lèi)型是不確定。其中可能包含的是String,也可能是 Integer。如果它包含了String的話(huà),往里面添加Integer類(lèi)型的元素就是錯(cuò)誤的。正因?yàn)轭?lèi)型未知,就不能通過(guò)new ArrayList<?>()的方法來(lái)創(chuàng)建一個(gè)新的ArrayList對(duì)象。因?yàn)榫幾g器無(wú)法知道具體的類(lèi)型是什么。但是對(duì)于 List<?>中的元素確總是可以用Object來(lái)引用的,因?yàn)殡m然類(lèi)型未知,但肯定是Object及其子類(lèi)。考慮下面的代碼:

  public void wildcard(List<?> list) {list.add(1); //編譯錯(cuò)誤 }

  如上所示,試圖對(duì)一個(gè)帶通配符的泛型類(lèi)進(jìn)行操作的時(shí)候,總是會(huì)出現(xiàn)編譯錯(cuò)誤。其原因在于通配符所表示的類(lèi)型是未知的。

  因?yàn)閷?duì)于List<?>中的元素只能用Object來(lái)引用,在有些情況下不是很方便。在這些情況下,可以使用上下界來(lái)限制未知類(lèi)型的范圍。 如List<? extends Number>說(shuō)明List中可能包含的元素類(lèi)型是Number及其子類(lèi)。而List<? super Number>則說(shuō)明List中包含的是Number及其父類(lèi)。當(dāng)引入了上界之后,在使用類(lèi)型的時(shí)候就可以使用上界類(lèi)中定義的方法。比如訪(fǎng)問(wèn) List<? extends Number>的時(shí)候,就可以使用Number類(lèi)的intValue等方法。

  開(kāi)發(fā)自己的泛型類(lèi)

  泛型類(lèi)與一般的Java類(lèi)基本相同,只是在類(lèi)和接口定義上多出來(lái)了用<>聲明的類(lèi)型參數(shù)。一個(gè)類(lèi)可以有多個(gè)類(lèi)型參數(shù),如 MyClass<X, Y, Z>。 每個(gè)類(lèi)型參數(shù)在聲明的時(shí)候可以指定上界。所聲明的類(lèi)型參數(shù)在Java類(lèi)中可以像一般的類(lèi)型一樣作為方法的參數(shù)和返回值,或是作為域和局部變量的類(lèi)型。但是由于類(lèi)型擦除機(jī)制,類(lèi)型參數(shù)并不能用來(lái)創(chuàng)建對(duì)象或是作為靜態(tài)變量的類(lèi)型。考慮下面的泛型類(lèi)中的正確和錯(cuò)誤的用法。

class ClassTest<X extends Number, Y, Z> { private X x; private static Y y; //編譯錯(cuò)誤,不能用在靜態(tài)變量中 public X getFirst() {return x; } public void wrong() { Z z = new Z(); //編譯錯(cuò)誤,不能創(chuàng)建對(duì)象 }}

?

參考資料:

  http://www.infoq.com/cn/articles/cf-java-generics

轉(zhuǎn)載于:https://www.cnblogs.com/shuaihua/archive/2013/01/17/2864509.html

超強(qiáng)干貨來(lái)襲 云風(fēng)專(zhuān)訪(fǎng):近40年碼齡,通宵達(dá)旦的技術(shù)人生

總結(jié)

以上是生活随笔為你收集整理的编译期间确定类型安全——泛型(Generics)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。