Java泛型之協(xié)變與逆變及extends與super選擇
要了解協(xié)變與逆變,首先要引入:
根據(jù) Liskov替換原則,如果C是P的子類,則P可以代替C,即 P p = new C();
C繼承于P,記做為 C < P
什么是不變
如果F是不變,當(dāng) C <= P 時(shí),那么 F(C) 和 F(P) 沒有任何繼承關(guān)系
除例如 Integer是 Number的子類,根據(jù) Liskov替換原則
Number number = new Integer(1); //correct
但是如果這樣寫就會(huì)報(bào)錯(cuò)
List<Number> list = new ArrayList<Integer>(1); //error
雖然 Number和 Integer存在繼承關(guān)系:Integer < Number, 但在Java里,泛型默認(rèn)是不變的, 因此也可以看作為 List<Number> 和List<Integer> 不存在任何繼承關(guān)系
什么是協(xié)變
如果F是協(xié)變的,當(dāng) C <= P 時(shí),那么 F(C) <= F(P)
Java 提供了一個(gè)extends來將不變轉(zhuǎn)為協(xié)變,例如:
List<? extends Number> list = new ArrayList<Integer>(1); //corrent
此時(shí)的List<? extends Number>可以看作為ArrayList<Integer>的父類
? extend Number可以看作為一個(gè)類型范圍,表示Number的某一個(gè)子類
數(shù)組默認(rèn)是協(xié)變的
Number[] numbers = new Integer[3];
什么是逆變
如果F是逆變的,當(dāng) C <= P 時(shí),那么 F(C) >= F(P)
Java 提供了一個(gè)super來將不變轉(zhuǎn)為協(xié)變,例如:
List<? super Number> list = new ArrayList<Object>(1); //corrent
此時(shí)的 List<? super Number>可以看作為 ArrayList<Object>的父類
extends 和 super
首先,我們看看Collection.add的實(shí)現(xiàn):
public interface List<E> extends Collection<E> { boolean add(E e); }下面代碼將會(huì)報(bào)錯(cuò)?? extends Number與Integer類型不匹配
List<? extends Number> list = new ArrayList<Integer>(); // correct list.add(Integer.valueOf(1)); //error
首先在調(diào)用add方法時(shí),泛型E自動(dòng)變成了<? extends Number>
第二行報(bào)錯(cuò),也就是說? extends Number不是Integer的父類。這里要將 List<? extends Number>是ArrayList<Integer>的父類區(qū)分開。
? extends Number可以看作為一個(gè)類型范圍中某一個(gè)類型,表示Number的某一個(gè)子類,但又沒明確是哪個(gè)子類,可能是Float,可能是Short,也可能是Integer的子類(Integer被final修飾,不可能有子類,這里只是一種假設(shè)情況),它只確定了它的上界為 Number,并沒有確定下界(有可能存在? extends Number< Integer),因此 ? extends Number不是Integer的父類
將上面代碼稍做修改就正確了:
List<? super Number> list = new ArrayList<Object>(); // correct list.add(Integer.valueOf(1)); //correct
首先因?yàn)槟孀儯?code>List<? super Number>是ArrayList<Object>的父類,第一行正確。
第二行: ? super Number是Integer的父類,原因是:? super Number表示Number的某一個(gè)父類,可能是Serializable也可能是 Object 但不管是哪個(gè),Number的父類一定是Integer的父類,因此第二行也正確
使用extends還是super呢
java.util.Collections的copy方法(JDK1.7)給了我們答案:
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}- 要從泛型類取數(shù)據(jù)時(shí),用extends;
- 要往泛型類寫數(shù)據(jù)時(shí),用super;
- 既要取又要寫,就不用通配符(即extends與super都不用)
private static <E> E getFirst(List<? extends E> list){
return list.get(0);
}
private static <E> void setFirst(List<? super E> list, E firstElement){
list.add(firstElement);
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
setFirst(list, 1);
Number number = getFirst(list);
}到此這篇關(guān)于Java泛型之協(xié)變與逆變及extends與super選擇的文章就介紹到這了,更多相關(guān)Java泛型 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MybatisPlus實(shí)現(xiàn)邏輯刪除功能
這篇文章主要介紹了MybatisPlus實(shí)現(xiàn)邏輯刪除功能,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
Jackson忽略字段實(shí)現(xiàn)對(duì)字段進(jìn)行序列化和反序列化
在使用?Jackson?進(jìn)行序列化和反序列化時(shí),有時(shí)候需要對(duì)某些字段進(jìn)行過濾,以便在?JSON?數(shù)據(jù)中不包含某些敏感信息,下面就一起來了解一下Jackson忽略字段實(shí)現(xiàn)對(duì)字段進(jìn)行序列化和反序2023-10-10
idea2019版Plugins中搜索不到任何插件的問題解決
本文主要介紹了idea2019版Plugins中搜索不到任何插件的問題解決,插件搜不出來的主要原因是plugins.jetbrains.com ping不通,下面就來介紹一下解決方法,感興趣的可以了解一下2023-09-09
SpringBoot使用Feign調(diào)用其他服務(wù)接口
這篇文章主要介紹了SpringBoot使用Feign調(diào)用其他服務(wù)接口,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
詳解Java的Proxy動(dòng)態(tài)代理機(jī)制
Java有兩種代理方式,一種是靜態(tài)代理,另一種是動(dòng)態(tài)代理。對(duì)于靜態(tài)代理,其實(shí)就是通過依賴注入,對(duì)對(duì)象進(jìn)行封裝,不讓外部知道實(shí)現(xiàn)的細(xì)節(jié)。很多 API 就是通過這種形式來封裝的2021-06-06
詳解Java設(shè)計(jì)模式編程中命令模式的項(xiàng)目結(jié)構(gòu)實(shí)現(xiàn)
這篇文章主要介紹了Java設(shè)計(jì)模式編程中命令模式的項(xiàng)目結(jié)構(gòu)實(shí)現(xiàn),命令模式將請(qǐng)求與執(zhí)行分離,可以多個(gè)命令接口的實(shí)現(xiàn)類,隱藏真實(shí)的被調(diào)用方,需要的朋友可以參考下2016-04-04

