博客地址:Java泛型:類型擦除
前情回顧Java泛型:泛型類、泛型接口和泛型方法
類型擦除 代碼片段一Class c1 = new ArrayList().getClass(); Class c2 = new ArrayList ().getClass(); System.out.println(c1 == c2); /* Output true */
顯然在平時(shí)使用中,ArrayList
這就是Java泛型的類型擦除造成的,因?yàn)椴还苁?b>ArrayList
在編譯器層面做的這件事(擦除具體的類型信息),使得Java的泛型先天都存在一個(gè)讓人非常難受的缺點(diǎn):
代碼片段二在泛型代碼內(nèi)部,無(wú)法獲得任何有關(guān)泛型參數(shù)類型的信息。
Listlist = new ArrayList (); Map map = new HashMap (); System.out.println(Arrays.toString(list.getClass().getTypeParameters())); System.out.println(Arrays.toString(map.getClass().getTypeParameters())); /* Output [E] [K, V] */
關(guān)于getTypeParameters()的解釋:
Returns an array of TypeVariable objects that represent the type variables declared by the generic declaration represented by this GenericDeclaration object, in declaration order. Returns an array of length 0 if the underlying generic declaration declares no type variables.
我們期待的是得到泛型參數(shù)的類型,但是實(shí)際上我們只得到了一堆占位符。
代碼片段三public class Main{ public T[] makeArray() { // error: Type parameter "T" cannot be instantiated directly return new T[5]; } }
我們無(wú)法在泛型內(nèi)部創(chuàng)建一個(gè)T類型的數(shù)組,原因也和之前一樣,T僅僅是個(gè)占位符,并沒(méi)有真實(shí)的類型信息,實(shí)際上,除了new表達(dá)式之外,instanceof操作和轉(zhuǎn)型(會(huì)收到警告)在泛型內(nèi)部都是無(wú)法使用的,而造成這個(gè)的原因就是之前講過(guò)的編譯器對(duì)類型信息進(jìn)行了擦除。
同時(shí),面對(duì)泛型內(nèi)部形如T var;的代碼時(shí),記得多念幾遍:它只是個(gè)Object,它只是個(gè)Object……
代碼片段四public class Main{ private T t; public void set(T t) { this.t = t; } public T get() { return t; } public static void main(String[] args) { Main m = new Main (); m.set("findingsea"); String s = m.get(); System.out.println(s); } } /* Output findingsea */
雖然有類型擦除的存在,使得編譯器在泛型內(nèi)部其實(shí)完全無(wú)法知道有關(guān)T的任何信息,但是編譯器可以保證重要的一點(diǎn):內(nèi)部一致性,也是我們放進(jìn)去的是什么類型的對(duì)象,取出來(lái)還是相同類型的對(duì)象,這一點(diǎn)讓Java的泛型起碼還是有用武之地的。
代碼片段四展現(xiàn)就是編譯器確保了我們放在t上的類型的確是T(即便它并不知道有關(guān)T的任何類型信息)。這種確保其實(shí)做了兩步工作:
set()處的類型檢驗(yàn)
get()處的類型轉(zhuǎn)換
這兩步工作也成為邊界動(dòng)作。
代碼片段五public class Main{ public List fillList(T t, int size) { List list = new ArrayList (); for (int i = 0; i < size; i++) { list.add(t); } return list; } public static void main(String[] args) { Main m = new Main (); List list = m.fillList("findingsea", 5); System.out.println(list.toString()); } } /* Output [findingsea, findingsea, findingsea, findingsea, findingsea] */
代碼片段五同樣展示的是泛型的內(nèi)部一致性。
擦除的補(bǔ)償如上看到的,但凡是涉及到確切類型信息的操作,在泛型內(nèi)部都是無(wú)法共工作的。那是否有辦法繞過(guò)這個(gè)問(wèn)題來(lái)編程,答案就是顯示地傳遞類型標(biāo)簽。
代碼片段六public class Main{ public T create(Class type) { try { return type.newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) { Main m = new Main (); String s = m.create(String.class); } }
代碼片段六展示了一種用類型標(biāo)簽生成新對(duì)象的方法,但是這個(gè)辦法很脆弱,因?yàn)檫@種辦法要求對(duì)應(yīng)的類型必須有默認(rèn)構(gòu)造函數(shù),遇到Integer類型的時(shí)候就失敗了,而且這個(gè)錯(cuò)誤還不能在編譯器捕獲。
進(jìn)階的方法可以用限制類型的顯示工廠和模板方法設(shè)計(jì)模式來(lái)改進(jìn)這個(gè)問(wèn)題,具體可以參見(jiàn)《Java編程思想 (第4版)》P382。
代碼片段七public class Main{ public T[] create(Class type) { return (T[]) Array.newInstance(type, 10); } public static void main(String[] args) { Main m = new Main (); String[] strings = m.create(String.class); } }
代碼片段七展示了對(duì)泛型數(shù)組的擦除補(bǔ)償,本質(zhì)方法還是通過(guò)顯示地傳遞類型標(biāo)簽,通過(guò)Array.newInstance(type, size)來(lái)生成數(shù)組,同時(shí)也是最為推薦的在泛型內(nèi)部生成數(shù)組的方法。
以上,泛型的第二部分的結(jié)束。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/64658.html
摘要:然而中的泛型使用了類型擦除,所以只是偽泛型??偨Y(jié)本文介紹了泛型的使用,以及類型擦除相關(guān)的問(wèn)題。一般情況下泛型的使用比較簡(jiǎn)單,但是某些情況下,尤其是自己編寫(xiě)使用泛型的類或者方法時(shí)要注意類型擦除的問(wèn)題。 簡(jiǎn)介 Java 在 1.5 引入了泛型機(jī)制,泛型本質(zhì)是參數(shù)化類型,也就是說(shuō)變量的類型是一個(gè)參數(shù),在使用時(shí)再指定為具體類型。泛型可以用于類、接口、方法,通過(guò)使用泛型可以使代碼更簡(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 提供了泛型概念,泛型使得開(kāi)發(fā)者可以定義較為安全的類型,不至于強(qiáng)制類型轉(zhuǎn)化時(shí)出現(xiàn)類型轉(zhuǎn)化異常,在沒(méi)有反省之前,可以通過(guò) Object...
摘要:可以看到,如果我們給泛型類制定了上限,泛型擦除之后就會(huì)被替換成類型的上限。相應(yīng)的,泛型類中定義的方法的類型也是如此。參考語(yǔ)言類型擦除下界通配符和的區(qū)別 本篇博客主要介紹了Java類型擦除的定義,詳細(xì)的介紹了類型擦除在Java中所出現(xiàn)的場(chǎng)景。 1. 什么是類型擦除 為了讓你們快速的對(duì)類型擦除有一個(gè)印象,首先舉一個(gè)很簡(jiǎn)單也很經(jīng)典的例子。 // 指定泛型為String List list1 ...
摘要:靜態(tài)變量是被泛型類的所有實(shí)例所共享的。所以引用能完成泛型類型的檢查。對(duì)于這個(gè)類型系統(tǒng),有如下的一些規(guī)則相同類型參數(shù)的泛型類的關(guān)系取決于泛型類自身的繼承體系結(jié)構(gòu)。事實(shí)上,泛型類擴(kuò)展都不合法。 前言 和C++以模板來(lái)實(shí)現(xiàn)靜多態(tài)不同,Java基于運(yùn)行時(shí)支持選擇了泛型,兩者的實(shí)現(xiàn)原理大相庭徑。C++可以支持基本類型作為模板參數(shù),Java卻只能接受類作為泛型參數(shù);Java可以在泛型類的方法中取得...
摘要:靜態(tài)變量是被泛型類的所有實(shí)例所共享的。對(duì)于這個(gè)類型系統(tǒng),有如下的一些規(guī)則相同類型參數(shù)的泛型類的關(guān)系取決于泛型類自身的繼承體系結(jié)構(gòu)。在代碼中避免泛型類和原始類型的混用。參考泛型類型擦除 Java泛型總結(jié) Java泛型是JDK5引入的一個(gè)新特性,允許在定義類和接口的時(shí)候使用類型參數(shù)(type parameter)。聲明的類型參數(shù)在使用的時(shí)候使用具體的類型來(lái)替換。泛型最主要的應(yīng)用是在JDK5...
閱讀 923·2021-10-13 09:39
閱讀 1515·2021-10-11 10:57
閱讀 2626·2019-08-26 13:53
閱讀 2572·2019-08-26 12:23
閱讀 3726·2019-08-23 18:30
閱讀 3777·2019-08-23 18:08
閱讀 2551·2019-08-23 18:04
閱讀 2982·2019-08-23 16:28