溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Java中?extendsT和?superT是什么

發(fā)布時間:2022-05-17 13:44:56 來源:億速云 閱讀:141 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“Java中?extendsT和?superT是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“Java中?extendsT和?superT是什么”吧!

? 通配符類型

  • <? extends T> 表示類型的上界,表示參數(shù)化類型的可能是T 或是 T的子類;

  • <? super T> 表示類型下界(Java Core中叫超類型限定),表示參數(shù)化類型是此類型的超類型(父類型),直至Object;

上界<? extends T>不能往里存,只能往外取

比如,我們現(xiàn)在定義:List<? extends T>首先你很容易誤解它為繼承于T的所有類的集合,你可能認(rèn)為,你定義的這個List可以用來put任何T的子類,那么我們看下面的代碼:

import java.util.LinkedList;
import java.util.List;

public class test {
    public static void main(String[] args) {
        List<? extends Father> list = new LinkedList<>();
        list.add(new Son());
    }
}
class Human{
}
class Father extends Human{
}
class Son extends Father{
}
class LeiFeng extends Father {
}

list.add(new Son());這行會報錯:The method put(Son) is undefined for the type List<capture#1-of ? extends Father>

List<? extends Father> 表示 “具有任何從Son繼承類型的列表”,編譯器無法確定List所持有的類型,所以無法安全的向其中添加對象。可以添加null,因為null 可以表示任何類型。所以List 的add 方法不能添加任何有意義的元素,但是可以接受現(xiàn)有的子類型List 賦值。

你也許試圖這樣做:

List<? extends Father> list = new LinkedList<Son>();
list.add(new Son());

即使你指明了為Son類型,也不能用add方法添加一個Son對象。

list中為什么不能加入Father類和Father類的子類呢,我們來分析下。

List<? extends Father>表示上限是Father,下面這樣的賦值都是合法的

   List<? extends Father> list1 = new ArrayList<Father>();
   List<? extends Father> list2 = new ArrayList<Son>();
   List<? extends Father> list3 = new ArrayList<LeiFeng>();

如果List<? extends Father>支持add方法的話:

  • list1可以add Father和所有Father的子類;

  • list2可以add Son和所有Son的子類;

  • list3可以add LeiFeng和所有LeiFeng的子類。

下面代碼是編譯不通過的:

list1.add(new Father());//error
list1.add(new Son());//error

原因是編譯器只知道容器內(nèi)是Father或者它的派生類,但具體是什么類型不知道??赡苁荈ather?可能是Son?也可能是LeiFeng,XiaoMing?編譯器在看到后面用Father賦值以后,集合里并沒有限定參數(shù)類型是“Father“。而是標(biāo)上一個占位符:CAP#1,來表示捕獲一個Father或Father的子類,具體是什么類不知道,代號CAP#1。然后無論是想往里插入Son或者LeiFeng或者Father編譯器都不知道能不能和這個CAP#1匹配,所以就都不允許。

所以通配符<?>和類型參數(shù)的區(qū)別就在于,對編譯器來說所有的T都代表同一種類型。比如下面這個泛型方法里,三個T都指代同一個類型,要么都是String,要么都是Integer。

public <T> List<T> fill(T... t);

但通配符<?>沒有這種約束,List<?>單純的就表示:集合里放了一個東西,是什么我不知道。

所以這里的錯誤就在這里,List<? extends Father>里什么都放不進(jìn)去。

List<? extends Father> list不能進(jìn)行add,但是,這種形式還是很有用的,雖然不能使用add方法,但是可以在初始化的時候一個Season指定不同的類型。比如:

List<? extends Father> list1 = getFatherList();//getFatherList方法會返回一個Father的子類的list

另外,由于我們已經(jīng)保證了List中保存的是Father類或者他的某一個子類,所以,可以用get方法直接獲得值:

List<? extends Father> list1 = new ArrayList<>();
Father father = list1.get(0);//讀取出來的東西只能存放在Father或它的基類里。
Object object = list1.get(0);//讀取出來的東西只能存放在Father或它的基類里。
Human human = list1.get(0);//讀取出來的東西只能存放在Father或它的基類里。
Son son = (Son)list1.get(0);

下界<? super T>不影響往里存,但往外取只能放在Object對象里

下界用super進(jìn)行聲明,表示參數(shù)化的類型可能是所指定的類型,或者是此類型的父類型,直至Object。

//super只能添加Father和Father的子類,不能添加Father的父類,讀取出來的東西只能存放在Object類里
List<? super Father> list = new ArrayList<>();
list.add(new Father());
list.add(new Human());//compile error 
list.add(new Son());
Father person1 = list.get(0);//compile error 
Son son = list.get(0);//compile error 
Object object1 = list.get(0);

因為下界規(guī)定了元素的最小粒度的下限,實際上是放松了容器元素的類型控制。既然元素是Father的基類,那往里存粒度比Father小的都可以。出于對類型安全的考慮,我們可以加入Father對象或者其任何子類(如Son)對象,但由于編譯器并不知道List的內(nèi)容究竟是Father的哪個超類,因此不允許加入特定的任何超類(如Human)。而當(dāng)我們讀取的時候,編譯器在不知道是什么類型的情況下只能返回Object對象,因為Object是任何Java類的最終祖先類。但這樣的話,元素的類型信息就全部丟失了。

PECS原則

最后看一下什么是PECS(Producer Extends Consumer Super)原則,已經(jīng)很好理解了:

  • 頻繁往外讀取內(nèi)容的,適合用上界Extends。

  • 經(jīng)常往里插入的,適合用下界Super。

到此,相信大家對“Java中?extendsT和?superT是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI