一.通配符的應(yīng)用
實(shí)踐檢驗(yàn)真理狂窑,所以在說(shuō)明通配符上下界的理解的時(shí)候垛玻,先說(shuō)明下什么時(shí)候使用通配符
關(guān)于泛型
類(lèi)型通配符的作用是為了代替泛型類(lèi)的類(lèi)型實(shí)參滥搭。
當(dāng)我們使用泛型類(lèi)作為參數(shù)時(shí)何什,我們不想固定具體的泛型類(lèi)型實(shí)參,而是想接收任意類(lèi)型咖楣,或者某個(gè)類(lèi)及其子類(lèi)或超類(lèi)類(lèi)型作為類(lèi)型實(shí)參督笆,這個(gè)時(shí)候就需要使用通配符了,使用泛型類(lèi)<?>
作為類(lèi)型實(shí)參
總結(jié)來(lái)說(shuō)通配符解決的問(wèn)題就是:類(lèi)B
是類(lèi)A
子類(lèi)诱贿,但是泛型類(lèi)<B>
不是泛型類(lèi)<A>
的子類(lèi)娃肿,但是我們又想有這么個(gè)類(lèi)型能同時(shí)接收泛型類(lèi)<B>
和泛型類(lèi)<A>
類(lèi)型作為參數(shù)的問(wèn)題
通配符類(lèi)型
無(wú)邊界通配符:<?>
使用無(wú)邊界通配符可以讓泛型接收任意類(lèi)型的數(shù)據(jù)上邊界通配符 :<?extends 具體類(lèi)型 >
使用固定上邊界的通配符的泛型可以接收指定類(lèi)型及其所有子類(lèi)類(lèi)型的數(shù)據(jù)珠十,這里的指定類(lèi)型可以是類(lèi)也可以是接口下邊界通配符 :<? spuer 具體類(lèi)型>
所有固定下邊界的通配符的泛型可以接收指定類(lèi)型及其所有超類(lèi)類(lèi)型的數(shù)據(jù)料扰。
通配符無(wú)法同時(shí)指定上下邊界
舉個(gè)栗子:
我們常用的List
就是一個(gè)泛型類(lèi),以java.lang.Number
類(lèi)及其子類(lèi)焙蹭,超類(lèi)作為類(lèi)型實(shí)參晒杈,具體繼承關(guān)系如下:
看下下面這段代碼:
private List<? extends Number> data;
private void test() {
List<Number> numbers = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
//List<Number> data = integers; //編譯報(bào)錯(cuò)
data = numbers;
data = integers;
}
顯然雖然Integer
是Number
的子類(lèi),但是List<Integer>
并不是List<Number>
的子類(lèi)孔厉,所以List<Integer>
類(lèi)型的變量不能直接賦值給List<Number>
的變量拯钻。這時(shí)候通配符就起到作用了List<? extends Number>
可以看做是List<Integer>
和List<Number>
的父類(lèi)型,它可以接收Number
類(lèi)或者其子類(lèi)型作為類(lèi)型形參的泛型數(shù)據(jù)烟馅。
所以當(dāng)我們要在一個(gè)方法或者類(lèi)中接收不固定類(lèi)型實(shí)參的泛型數(shù)據(jù)说庭,可以考慮使用通配符
二.關(guān)于通配符的上下界
1. 無(wú)邊界 和 上邊界通配符
使用無(wú)邊界和上邊界通配符的泛型不能賦值(除了null)然磷,可以取值郑趁,但是只能去指定的類(lèi)型及其超類(lèi)類(lèi)型(無(wú)邊界只能取Object類(lèi)型數(shù)據(jù))
(無(wú)邊界其實(shí)上邊界就是Object)
以List
為例:下面這段代碼當(dāng)我們用List<?>
或者List<? extends Number>
add數(shù)據(jù)時(shí)發(fā)現(xiàn)都會(huì)編譯報(bào)錯(cuò)。
為什么:根據(jù)上面應(yīng)用的結(jié)論姿搜,List<Integer>
寡润、List<Long>
捆憎、List<Double>
這些類(lèi)型可以理解為List<?>
或者List<? extends Number>
的子類(lèi)型。
這時(shí)候List<?>
或者List<? extends Number>
add
時(shí)候不知道到底要往List<Integer>
梭纹、List<Long>
躲惰、List<Double>
還是其他Number
子類(lèi)型的List
中add
數(shù)據(jù)的是Integer
、Long
還是Double
類(lèi)型变抽。這么操作可能會(huì)引發(fā)類(lèi)型不一致的問(wèn)題础拨,這顯然和泛型的設(shè)計(jì)是相悖的。因此Java為了保證類(lèi)型一致绍载,是不允許這么操作的诡宗。但是null是所有引用類(lèi)型都有元素,所有可以add
成功击儡。
List<?> data = new ArrayList<>();
List<?> data = new ArrayList<>();
data.add(new Object()); //編譯報(bào)錯(cuò)
data.add(10); //編譯報(bào)錯(cuò)
data.add(null);
List<? extends Number> numbers = new ArrayList<>();
numbers.add(10); //編譯報(bào)錯(cuò)
numbers.add(new BigDecimal(20)); //編譯報(bào)錯(cuò)
numbers.add(null);
在看一下get
取值方法:
下面代碼可以看出(忽略運(yùn)行錯(cuò)誤啊塔沃,只是為了說(shuō)明泛型編譯問(wèn)題):get
方法是可以取到指定類(lèi)型及其超類(lèi)型的數(shù)據(jù)。
List<?> data = new ArrayList<>();
Object object = data.get(0);
List<? extends Number> numbers = new ArrayList<>();
Number number = numbers.get(0);
Object number2 = numbers.get(0);
2.下邊界通配符
與無(wú)邊界和上邊界通配符相反阳谍,下邊界通配符只能取Object類(lèi)型的數(shù)據(jù)蛀柴,但可以賦值,只要是指定類(lèi)型或者其子類(lèi)型都能成功賦值
還是以List
類(lèi)為例矫夯,代碼如下鸽疾,對(duì)應(yīng)到List
里面就是add
指定類(lèi)型及其子類(lèi)型數(shù)據(jù)時(shí)可以正常編譯通過(guò),但是get
方法不能編譯通過(guò)
為什么:因?yàn)?code>Long训貌、BigDecimal
肮韧、Float
都是Numer
的子類(lèi),根據(jù)之前的結(jié)論? super Number
代表可以接收指定類(lèi)型及其父類(lèi)型的數(shù)據(jù)旺订,所以List<? super Number>
可以理解為List<Number>
或者List<Object>
的父類(lèi)型弄企,顯然List<Number>
或者List<Object>
是可以add
Numer
的子類(lèi)型數(shù)據(jù)的。但是get
的時(shí)候因?yàn)椴恢谰唧w是
List<Number>
還是List<Object>或者是之間的什么類(lèi)型区拳,所以只能
get所以類(lèi)型的父類(lèi)型
Object`類(lèi)型
List<? super Number> data = new ArrayList<>();
data.add(10);
data.add(new BigDecimal(1000));
data.add(10.07f);
data.add(new Object()) //編譯報(bào)錯(cuò)
Object object = data.get(0);
Number number = data.get(0); //編譯報(bào)錯(cuò)