摘要:泛型之上界下界通配符本教程是為編寫的。這是在使用泛型編程時(shí)一個(gè)常見的誤解,也是一個(gè)需要學(xué)習(xí)的重要概念。通配符使用指南學(xué)習(xí)使用泛型編程時(shí),更令人困惑的一個(gè)方面是確定何時(shí)使用上限有界通配符以及何時(shí)使用下限有界通配符。
Java 泛型之上界下界通配符
本Java教程是為JDK 8編寫的。本頁(yè)描述的示例和實(shí)踐沒有利用后續(xù)版本中引入的改進(jìn)。泛型,繼承和子類
如你所知,只要類型兼容,就可以將一種類型的對(duì)象分配給另一種類型的對(duì)象。例如,你可以指定一個(gè)整數(shù)一個(gè)對(duì)象,因?yàn)閷?duì)象是一個(gè)整數(shù)的超類型:
Object someObject = new Object(); Integer someInteger = new Integer(10); someObject = someInteger; // 好
在面向?qū)ο蟮男g(shù)語(yǔ)中,這被稱為“是一種”關(guān)系。由于Integer 是一種Object,因此允許賦值。但是Integer也是一種Number,所以下面的代碼也是有效的:
public void someMethod(Number n){/ * ... * /} someMethod(new Integer(10)); // 好 someMethod(new Double(10.1)); // 好
泛型也是如此。您可以執(zhí)行泛型類型調(diào)用,將Number作為其類型參數(shù)傳遞,如果參數(shù)與Number兼容,則允許任何后續(xù)的add調(diào)用:
Box
box.add(new Integer(10)); // 好
box.add(new Double(10.1)); // 好
現(xiàn)在考慮以下方法:
public void boxTest(Boxn){/ * ... * /}
它接受什么類型的論據(jù)?通過查看其簽名,您可以看到它接受一個(gè)類型為Box
這是在使用泛型編程時(shí)一個(gè)常見的誤解,也是一個(gè)需要學(xué)習(xí)的重要概念。
Box
注意:給定兩個(gè)具體類型 A 和 B(例如,Number和Integer),MyClass 與 MyClass無關(guān),無論 A 和 B 是否相關(guān)。MyClass 和 MyClass 的公共父是Object。
有關(guān)如何在類型參數(shù)相關(guān)時(shí)在兩個(gè)泛型類之間創(chuàng)建類似子類型關(guān)系的信息,請(qǐng)參閱下面的通配符和子類型一節(jié)。
泛型類和子類型化您可以通過擴(kuò)展(extends)泛型類或?qū)崿F(xiàn)(implements)泛型接口來對(duì)其進(jìn)行子類型化。一個(gè)類或接口的類型參數(shù)與另一個(gè)類或接口的類型參數(shù)之間的關(guān)系由extends和implements子句確定。
使用Collections類作為示例,ArrayList
Collection層次結(jié)構(gòu)示例
顯示Collection層次結(jié)構(gòu)示例的圖表:ArrayList是 List 的子類型,二者都是Collection 的子類型。
現(xiàn)在假設(shè)我們想要定義我們自己的列表接口PayloadList,它將可選值泛型類型參數(shù)P的與每個(gè)元素相關(guān)聯(lián)。它的聲明可能如下:
interface PayloadListextends List { void setPayload(int index, P val); ... }
PayloadList的以下參數(shù)化是List
PayloadListPayloadList PayloadList
PayloadList層次結(jié)構(gòu)示例
PayLoadList層次結(jié)構(gòu)的示意圖:PayloadList通配符和子類型是List 的子類型,它是Collection 的子類型。 在PayloadList 的相同級(jí)別是PayloadList 和PayloadList 。
如 泛型,繼承和子類一節(jié)中所述,泛型類之間或接口之間幾乎并不因它們的類型參數(shù)而相關(guān)。但是,您可以使用通配符在泛型類或接口之間創(chuàng)建關(guān)系。
給定以下兩個(gè)常規(guī)(非泛型)類:
class A { /* ... */ } class B extends A { /* ... */ }
編寫以下代碼是合理的:
B b = new B(); A a = b;
此示例顯示常規(guī)類的繼承遵循此子類型規(guī)則:如果B擴(kuò)展A,則類B是類A的子類型。此規(guī)則不適用于泛型類型:
List lb = new ArrayList<>(); List la = lb; //編譯時(shí)錯(cuò)誤
鑒于Integer是Number的子類型,List
公共父類是List>
該圖表顯示 List和 List 的公共父級(jí)是未知類型的List.
盡管Integer是Number的子類型,但List
為了在這些類之間創(chuàng)建關(guān)系以便代碼可以通過 List
List extends Integer> intList = new ArrayList<>(); List extends Number> numList = intList; // OK, List<?extends Integer>是 List< ? extends Number>的子類型
因?yàn)?b>Integer是Number的子類型,而numList是Number對(duì)象的列表,所以intList(是一個(gè)Integer對(duì)象列表)和numList之間現(xiàn)在存在關(guān)系。下圖顯示了使用上限和下限通配符聲明的多個(gè) List 類之間的關(guān)系。
幾個(gè)通用List類聲明的層次結(jié)構(gòu)。
圖表顯示: List通配符使用指南是List extends Integer>和 List<?super Integer>的子類型。 List extends Integer>是List extends Number> 的子類型,它是List >的子類型。 List 是List <?super Number> 和 List extends Number>的子類型。 List super Number>是List super Integer>的子類型, 且都是List>的子類型。
學(xué)習(xí)使用泛型編程時(shí),更令人困惑的一個(gè)方面是確定何時(shí)使用上限有界通配符以及何時(shí)使用下限有界通配符。本文提供一些設(shè)計(jì)代碼時(shí)要遵循的一些準(zhǔn)則。
為討論方便,認(rèn)為變量具備兩個(gè)功能:
一個(gè)“In”變量
“in”變量向代碼提供數(shù)據(jù)。想象一下帶有兩個(gè)參數(shù)的復(fù)制方法:copy(src,dest)。該SRC參數(shù)提供的數(shù)據(jù)被復(fù)制,因此它是“in”參數(shù)。
一個(gè)“Out”變量
“out”變量保存數(shù)據(jù)以供其他地方使用。在復(fù)制示例中,copy(src,dest),dest參數(shù)接受數(shù)據(jù),因此它是“out”參數(shù)。
當(dāng)然,一些變量既用于“in”又用于“out”目的 - 這種情況也在本文中也用到了。
在決定是否使用通配符以及適合使用哪種類型的通配符時(shí),可以使用“in”和“out”原則。以下列表提供了遵循的準(zhǔn)則:
通配符指南:
使用extends關(guān)鍵字, 定義帶有上界通配符的“in”變量。
使用super關(guān)鍵字,使用下界通配符定義“out”變量。
在可以使用Object類中定義的方法訪問“in”變量的情況下,使用無界通配符。
在代碼需要作為“in”和“out”變量訪問變量的情況下,不要使用通配符。
這些指南不適用于方法的返回類型。應(yīng)該避免使用通配符作為返回類型,因?yàn)樗鼜?qiáng)制程序員使用代碼來處理通配符。
List extends ...> 可以被非正式地認(rèn)為是只讀的,但這不是一個(gè)嚴(yán)格的保證。假設(shè)您有以下兩個(gè)類:
class NaturalNumber { private int i; public NaturalNumber(int i) { this.i = i; } // ... } class EvenNumber extends NaturalNumber { public EvenNumber(int i) { super(i); } // ... }
請(qǐng)考慮以下代碼:
Listle = new ArrayList<>(); List extends NaturalNumber> ln = le; ln.add(new NaturalNumber(35)); // compile-time error //編譯時(shí)錯(cuò)誤
因?yàn)?b>List
您可以添加null。
你可以調(diào)用清除。
您可以獲取迭代器并調(diào)用remove。
您可以捕獲通配符并寫入從列表中讀取的元素。
你可以看到List extends NaturalNumber>在嚴(yán)格意義上不是只讀的,但您可能會(huì)這樣想,因?yàn)槟鸁o法存儲(chǔ)新元素或更改列表中的現(xiàn)有元素。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/77808.html
泛型通配符 在泛型代碼中,稱為通配符的問號(hào)(?)表示未知類型,通配符可用于各種情況:作為參數(shù)、字段或局部變量的類型,有時(shí)作為返回類型(盡管更好的編程實(shí)踐是更加具體),通配符從不用作泛型方法調(diào)用、泛型類實(shí)例創(chuàng)建或超類型的類型參數(shù)。 以下部分更詳細(xì)地討論通配符,包括上界通配符、下界通配符和通配符捕獲。 上界通配符 你可以使用上界通配符來放寬對(duì)變量的限制,例如,假設(shè)你要編寫一個(gè)適用于List、List和...
泛型通配符使用指南 學(xué)習(xí)使用泛型編程時(shí)更困惑的一個(gè)方面是確定何時(shí)使用上界通配符以及何時(shí)使用下界通配符,此頁(yè)面提供了設(shè)計(jì)代碼時(shí)要遵循的一些準(zhǔn)則。 對(duì)于本文的討論,將變量看作提供的兩個(gè)功能之一是有幫助的: 一個(gè)In變量 in變量向代碼提供數(shù)據(jù),想象一下帶有兩個(gè)參數(shù)的復(fù)制方法:copy(src, dest),src參數(shù)提供要復(fù)制的數(shù)據(jù),因此它是in參數(shù)。 一個(gè)Out變量 out變量保存數(shù)據(jù)以供其他地方使...
摘要:靜態(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í)候使用具體的類型來替換。泛型最主要的應(yīng)用是在JDK5...
摘要:調(diào)用相當(dāng)于通過使用通配符,可以傳遞任何類型的對(duì)象,但也是有缺點(diǎn)的。使用通配符,賦值傳值的時(shí)候方便了,但是對(duì)泛型類中參數(shù)為泛型的方法起到了副作用。結(jié)論當(dāng)使用父界限定通配符時(shí),泛型類中返回值為泛型的方法不能使用。 數(shù)組 VS List 第一回合數(shù)組類型為Object,可以存儲(chǔ)任意類型的對(duì)象,List集合同樣可以做到 Object[] obj = new Object[1]; List li...
摘要:虛擬機(jī)中并沒有泛型類型對(duì)象,所有的對(duì)象都是普通類。其原因就是泛型的擦除。中數(shù)組是協(xié)變的,泛型是不可變的。在不指定泛型的情況下,泛型變量的類型為該方法中的幾種類型的同一個(gè)父類的最小級(jí),直到。 引入泛型的主要目標(biāo)有以下幾點(diǎn): 類型安全 泛型的主要目標(biāo)是提高 Java 程序的類型安全 編譯時(shí)期就可以檢查出因 Java 類型不正確導(dǎo)致的 ClassCastException 異常 符合越早出...
閱讀 3168·2023-04-25 18:22
閱讀 2413·2021-11-17 09:33
閱讀 3334·2021-10-11 10:59
閱讀 3250·2021-09-22 15:50
閱讀 2827·2021-09-10 10:50
閱讀 870·2019-08-30 15:53
閱讀 460·2019-08-29 11:21
閱讀 2930·2019-08-26 13:58