Java泛型中的通配符<?>杨耙,<? extends T>赤套,<? super T>

之前一直對(duì)Java泛型中的通配符不是很清楚,前幾天專(zhuān)門(mén)研究了一下珊膜。
Java中的泛型通配符分為以下三種:

  • <? extends T> 子類(lèi)型限定通配符
  • <? super T> 超類(lèi)型限定通配符
  • <?> 無(wú)限定通配符

通配符的使用場(chǎng)景

通配符只有在修飾一個(gè)變量參數(shù)的時(shí)候會(huì)用到容握,在定義泛型類(lèi)或泛型方法的時(shí)候是不能使用通配符的。

為了更好的說(shuō)明泛型通配符的使用车柠,我們使用代碼示例來(lái)加以說(shuō)明剔氏。首先我們創(chuàng)建一個(gè)類(lèi) A,是一個(gè)泛型類(lèi)竹祷,里面保存一個(gè)變量 value

public class A<T> {
    private T value;
    // 省略 get 和 set 方法
    // ......
}

我們?cè)賱?chuàng)建兩個(gè)類(lèi) Father 和 Son谈跛,Son 是 Father 的子類(lèi)

// Father.java
public class Father {
}

// Son.java
public class Son extends Father{
}

思考下面的情況

public static void main(String[] args){
    A<Father> a1 = new A<Father>();
    A<Son> a2 = new A<Son>();
    test(a1);
    test(a2);  // 編譯錯(cuò)誤
}

public void test(A<Father> a){...}

Son 是 Father 的子類(lèi),但 test(A<Father> a) 方法卻不能接收參數(shù) a2塑陵,也就是說(shuō) A<Son> 不是 A<Father> 的子類(lèi)感憾。這個(gè)時(shí)候就可以使用通配符來(lái)解決,我們修改 test 方法

public static void main(String[] args){
    A<Father> a1 = new A<Father>();
    A<Son> a2 = new A<Son>();
    test(a1);
    test(a2);
}
public void test(A<? extends Father> a){...}

這樣可以可以正常調(diào)用猿妈,說(shuō)明類(lèi)型 A<Son>A<? extends Father> 的子類(lèi)型

我們清楚了通配符的使用場(chǎng)景吹菱,下面再分別看下幾種通配符

<? extends T> 子類(lèi)型限定通配符

我們上面的例子使用了子類(lèi)型限定通配符,使用通配符很方便彭则,但也帶來(lái)了一些問(wèn)題鳍刷,我們接著看代碼

public void test(A<? extends Father> a){
    a.setValue(new Father()); //編譯錯(cuò)誤
    a.setValue(new Son()); //編譯錯(cuò)誤
    Father father = a.getValue();
}

你會(huì)發(fā)現(xiàn)我們根本不能調(diào)用 setValue 方法,假想一下 A<? extends Father> 類(lèi)俯抖,里面的方法似乎是這樣的

// 這不是真正的Java方法输瓜,只是為了說(shuō)明
? extends Father getValue();
void setValue(? extends Father);

當(dāng)我們調(diào)用 setValue 方法的時(shí)候,編譯器只知道需要某個(gè) Father 及其子類(lèi)型,但不知道具體是什么類(lèi)型尤揣,所以它拒絕傳遞任何的特定類(lèi)型搔啊。

使用 getValue 就不會(huì)有問(wèn)題,因?yàn)榉祷刂悼隙ㄊ?Father 及其子類(lèi)型北戏,所以我們把返回值賦給一個(gè) Father 的引用完全合法负芋。

<? super T> 超類(lèi)型限定通配符

超類(lèi)型限定通配符的行為與上面說(shuō)的子類(lèi)型限定通配符相反,可以為方法提供參數(shù)嗜愈,但不能使用返回值旧蛾。我們看下面的例子:

public void test2(A<? super Father> a){
    a.setValue(new Father());
    Father father = a.getValue(); // 編譯錯(cuò)誤
    Object object = a.getValue(); 
}

假想一下 A<? super Father> 類(lèi),里面的方法似乎是這樣的

// 這不是真正的Java方法蠕嫁,只是為了說(shuō)明
void setValue(? super Father)
? super Father getValue() 

setValue 方法不知道參數(shù)的具體類(lèi)型锨天,但是可以確定的是參數(shù)肯定是 Father 及其父類(lèi)型,所以我們傳遞 Father 及其子類(lèi)型是合法的剃毒。

調(diào)用 getValue 方法不能保證返回類(lèi)型的對(duì)象病袄,所以只能賦給一個(gè) Object。

<?> 無(wú)限定通配符

還可以使用無(wú)限定通配符 <?> 赘阀,這種方式益缠,不能為方法提供參數(shù),調(diào)用方法返回值也只能賦給 Object纤壁。

public void test3(A<?> a){
    Object object = a.getValue();
    a.setValue(new Object()); //編譯錯(cuò)誤
}

getValue 的返回值只能賦給一個(gè) Object左刽,setValue 方法不能被調(diào)用(注意:可以調(diào)用 setValue(null))。

所以感覺(jué)無(wú)限定通配符是集合了上面說(shuō)的兩種通配符的缺點(diǎn)酌媒,那我們?yōu)槭裁催€要使用它呢欠痴?其實(shí)在某些簡(jiǎn)單的場(chǎng)景還是有用的,例如下面這種情況

// 判斷A類(lèi)中的值是否為空秒咨,并不關(guān)心A類(lèi)中值具體是什么類(lèi)型
public Boolean isNull(A<?> a){
    return a.getValue == null;
}

總結(jié)

看完了三種通配符的使用喇辽,我們來(lái)做個(gè)總結(jié):

  • <? extends T> 子類(lèi)型限定通配符
    無(wú)法向其中設(shè)置值,但是可以進(jìn)行正常的取出
  • <? super T> 父類(lèi)型限定通配符
    可以設(shè)置 T 類(lèi)型及其子類(lèi)型的對(duì)象雨席,但是取出的時(shí)候只能賦值給 Object
  • <?> 無(wú)限定通配符
    無(wú)法向其中設(shè)置值菩咨,取值的時(shí)候也只能賦值給 Object

從上面的總結(jié)可以看出,<? extends T> 通配符偏向于內(nèi)容的獲取陡厘,而 <? super T> 通配符更偏向于內(nèi)容的存入抽米。

PECS 原則(Producer Extends Consumer Super)很好的解釋了這兩種通配符的使用場(chǎng)景:

  • Producer Extends 說(shuō)的是當(dāng)你的情景是生產(chǎn)者類(lèi)型,需要獲取資源以供生產(chǎn)時(shí)糙置,建議使用 extends 通配符云茸,因?yàn)槭褂昧?extends 通配符的類(lèi)型更適合獲取資源。
  • Consumer Super 說(shuō)的是當(dāng)你的場(chǎng)景是消費(fèi)者類(lèi)型谤饭,需要存入資源以供消費(fèi)時(shí)标捺,建議使用 super 通配符懊纳,因?yàn)槭褂?super 通配符的類(lèi)型更適合存入資源。

當(dāng)然亡容,如果你既想設(shè)置值又想取出值嗤疯,那么就不適合使用通配符了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末闺兢,一起剝皮案震驚了整個(gè)濱河市茂缚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌屋谭,老刑警劉巖阱佛,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異戴而,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)翩蘸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)所意,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人催首,你說(shuō)我怎么就攤上這事扶踊。” “怎么了郎任?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵秧耗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我舶治,道長(zhǎng)分井,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任霉猛,我火速辦了婚禮尺锚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘惜浅。我一直安慰自己瘫辩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布坛悉。 她就那樣靜靜地躺著伐厌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪裸影。 梳的紋絲不亂的頭發(fā)上挣轨,一...
    開(kāi)封第一講書(shū)人閱讀 51,115評(píng)論 1 296
  • 那天,我揣著相機(jī)與錄音空民,去河邊找鬼刃唐。 笑死羞迷,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的画饥。 我是一名探鬼主播衔瓮,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼抖甘!你這毒婦竟也來(lái)了热鞍?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤衔彻,失蹤者是張志新(化名)和其女友劉穎薇宠,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體艰额,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡澄港,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了柄沮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片回梧。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖祖搓,靈堂內(nèi)的尸體忽然破棺而出狱意,到底是詐尸還是另有隱情,我是刑警寧澤拯欧,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布详囤,位于F島的核電站,受9級(jí)特大地震影響镐作,放射性物質(zhì)發(fā)生泄漏藏姐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一该贾、第九天 我趴在偏房一處隱蔽的房頂上張望包各。 院中可真熱鬧,春花似錦靶庙、人聲如沸问畅。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)护姆。三九已至,卻和暖如春掏击,著一層夾襖步出監(jiān)牢的瞬間卵皂,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工砚亭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留灯变,地道東北人殴玛。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像添祸,于是被迫代替她去往敵國(guó)和親滚粟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353