【The Java? Tutorials】【Generics】7. Wildcards

在泛型代碼中逢防,?被稱為通配符,用于表示未知類型∷┎猓可被用于:the type of a parameter, field, or local variable; sometimes as a return type;不可被用于: the type argument for a generic method invocation, a generic class instance creation, or a supertype府蛇。

Upper Bounded Wildcards

我們來看這么一個(gè)場景:你希望函數(shù)foo可以接受List<Number>和List<Number的子類>作為參數(shù)集索。因?yàn)長ist<Number的子類>不是List<Number>的子類,所以不能簡單的把函數(shù)定義為foo(List<Number> list)汇跨。這個(gè)時(shí)候Upper Bounded Wildcards就派上了用場务荆。

public static void foo(List<? extends Number> list) { /* ... */ }

這個(gè)時(shí)候list的元素可以調(diào)用Number中定義的方法:

public static double foo(List<? extends Number> list) {
    double s = 0.0;
    for (Number n : list)
        s += n.doubleValue();
    return s;
}

這個(gè)時(shí)候foo可以接收List<Number的子類>作為參數(shù):

List<Integer> li = Arrays.asList(1, 2, 3);
System.out.println("sum = " + sumOfList(li));

List<Double> ld = Arrays.asList(1.2, 2.3, 3.5);
System.out.println("sum = " + sumOfList(ld));

Unbounded Wildcards

一個(gè)常見的unbounded wildcards的例子是Class<?>。什么情況會(huì)用到unbounded wildcards呢:

  • 你只需要用到Object提供的方法
  • 你的代碼和參數(shù)類型沒有關(guān)系穷遂,比如:List.size, List.clear函匕。

我們先來看下面這段代碼:

public static void printList(List<Object> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

printList可以打印任意類型的list,但是printList不能接受List<Integer>, List<String>等類型蚪黑,因?yàn)樗鼈儾皇荓ist<Object>的子類盅惜。用unbounded wildcards改寫printList就可以解決上述問題:

public static void printList(List<?> list) {
    for (Object elem: list)
        System.out.print(elem + " ");
    System.out.println();
}

注意:List<Object>和List<?>是不同的,你可以添加任意類實(shí)例到List<Object>列表中祠锣,但是你只能添加null到List<?>列表中酷窥。

Lower Bounded Wildcards

場景:Say you want to write a method that puts Integer objects into a list. To maximize flexibility, you would like the method to work on List<Integer>, List<Number>, and List<Object> — anything that can hold Integer values.

public static void addNumbers(List<? super Integer> list) {
    for (int i = 1; i <= 10; i++) {
        list.add(i);
    }
}

Wildcards and Subtyping

假設(shè)有以下兩個(gè)類:

class A { /* ... */ }
class B extends A { /* ... */ }

像下面這么寫是合理的:

B b = new B();
A a = b;

但是如果像下面這么寫就會(huì)報(bào)錯(cuò):

List<B> lb = new ArrayList<>();
List<A> la = lb;   // compile-time error

也就是說雖然B是A的子類,但是List<B>卻不是List<A>的子類伴网。那他們之間有什么關(guān)系呢:

List<Number>和List<Integer>的公共父類

除此之外蓬推,還有以下層級(jí)關(guān)系:


A hierarchy of several generic List class declarations

Wildcard Capture and Helper Methods

In some cases, the compiler infers the type of a wildcard. For example, a list may be defined as List<?> but, when evaluating an expression, the compiler infers a particular type from the code. This scenario is known as wildcard capture.

For the most part, you don't need to worry about wildcard capture, except when you see an error message that contains the phrase "capture of".

我們先來看一個(gè)WildcardError的例子:

import java.util.List;

public class WildcardError {

    void foo(List<?> i) {
        i.set(0, i.get(0));
    }
}

編譯上面的代碼有出現(xiàn)如下錯(cuò)誤:Error:(9, 22) java: 不兼容的類型: java.lang.Object無法轉(zhuǎn)換為capture#1, 共 ?

可以用以下方法解決上述問題,編譯器可以推斷出T是capture#1:

public class WildcardFixed {

    void foo(List<?> i) {
        fooHelper(i);
    }


    // Helper method created so that the wildcard can be captured
    // through type inference.
    private <T> void fooHelper(List<T> l) {
        l.set(0, l.get(0));
    }

}

現(xiàn)在來看一段更復(fù)雜的情況:

import java.util.List;

public class WildcardErrorBad {

    void swapFirst(List<? extends Number> l1, List<? extends Number> l2) {
      Number temp = l1.get(0);
      l1.set(0, l2.get(0)); // expected a CAP#1 extends Number,
                            // got a CAP#2 extends Number;
                            // same bound, but different types
      l2.set(0, temp);      // expected a CAP#1 extends Number,
                            // got a Number
    }
}

編譯上面的代碼會(huì)得到如下錯(cuò)誤:
Error:(10, 25) java: 不兼容的類型: java.lang.Number無法轉(zhuǎn)換為capture#1, 共 ? extends java.lang.Number
Error:(13, 19) java: 不兼容的類型: java.lang.Number無法轉(zhuǎn)換為capture#2, 共 ? extends java.lang.Number

我們應(yīng)該注意到澡腾,這種編譯錯(cuò)誤是合理的沸伏。假如l1是Integer的List糕珊,l2是Double的List,交換這個(gè)兩個(gè)List中的元素顯然是不合理的毅糟。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末红选,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子姆另,更是在濱河造成了極大的恐慌喇肋,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迹辐,死亡現(xiàn)場離奇詭異蝶防,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)明吩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門间学,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人印荔,你說我怎么就攤上這事低葫。” “怎么了仍律?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵嘿悬,是天一觀的道長。 經(jīng)常有香客問我染苛,道長鹊漠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任茶行,我火速辦了婚禮躯概,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘畔师。我一直安慰自己娶靡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布看锉。 她就那樣靜靜地躺著姿锭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪伯铣。 梳的紋絲不亂的頭發(fā)上呻此,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音腔寡,去河邊找鬼焚鲜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的忿磅。 我是一名探鬼主播糯彬,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼葱她!你這毒婦竟也來了撩扒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤吨些,失蹤者是張志新(化名)和其女友劉穎搓谆,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锤灿,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挽拔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了但校。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡啡氢,死狀恐怖状囱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情倘是,我是刑警寧澤亭枷,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站搀崭,受9級(jí)特大地震影響叨粘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瘤睹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一升敲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧轰传,春花似錦驴党、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至恕曲,卻和暖如春鹏氧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背佩谣。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來泰國打工把还, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓笨篷,卻偏偏與公主長得像瞳秽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子率翅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 對象的創(chuàng)建與銷毀 Item 1: 使用static工廠方法练俐,而不是構(gòu)造函數(shù)創(chuàng)建對象:僅僅是創(chuàng)建對象的方法,并非Fa...
    孫小磊閱讀 1,986評(píng)論 0 3
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,336評(píng)論 0 10
  • 《Effective Java》筆記(上) 對象的創(chuàng)建與銷毀 Item 1: 使用static工廠方法冕臭,而不是構(gòu)造...
    OCNYang閱讀 2,594評(píng)論 2 17
  • 拷貝腺晾,顧名思義就是復(fù)制,分配新的內(nèi)存辜贵,產(chǎn)生新的對象悯蝉,與new關(guān)鍵字類似。然而托慨,拷貝中存在部分隱匿的差別鼻由,需要我們細(xì)...
    采風(fēng)JS閱讀 623評(píng)論 0 40
  • 論析昆曲曲牌音樂之體制規(guī)律及其變異─以《牡丹亭?游園》為例─ 黃 慧 玲 摘 要 昆曲是我國明、清時(shí)期重要的戲曲唱...
    閶門夢斷閱讀 1,208評(píng)論 0 0