無(wú)界通配符

無(wú)界通配符

無(wú)界通配符<?> 看起來(lái)意味著“任何事物”,因此使用無(wú)界通配符好像等價(jià)于使用原生類(lèi)型。而事實(shí)上,編譯器初看起來(lái)是支持這種判斷的珍德。

public class UnBoundedWildcards {
    static List list1;
    static List<?> list2;
    static List<? extends Object> list3;

    static void assign1(List list) {
        list1 = list;
        list2 = list;

        // warning: unchecked conversion
        // Found: List, Require: List<? extends Object>
        list3 = list;
    }

    static void assign2(List<?> list) {
        list1 = list;
        list2 = list;
        list3 = list;
    }

    static void assign3(List<? extends Object> list) {
        list1 = list;
        list2 = list;
        list3 = list;
    }


    public static void main(String[] args) {
        assign1(new ArrayList());
        assign2(new ArrayList());
        // warning: unchecked conversion
        // Found: ArrayList, Require: List<? extends Object>
        assign3(new ArrayList());

        assign1(new ArrayList<String>());
        assign2(new ArrayList<String>());
        assign3(new ArrayList<String>());

        List<?> wildList = new ArrayList();
        assign1(wildList);
        assign2(wildList);
        assign3(wildList);
    }
}

編譯器很少關(guān)心使用的是原生類(lèi)型還是<?>,而在這種情況下矗漾,<?>可以被認(rèn)為是一種裝飾锈候,但是它仍舊是很有價(jià)值的,因?yàn)槌ü保瑢?shí)際上泵琳,它是在聲明:“想用Java的泛型來(lái)編寫(xiě)代碼,但是我在這里并不是要用原生類(lèi)型誊役,但是在當(dāng)前這種情況下获列,泛型參數(shù)可以持有任何類(lèi)型』坠福”


下面的示例展示了無(wú)界通配符的一個(gè)重要的應(yīng)用击孩。當(dāng)你處理多個(gè)泛型參數(shù)的時(shí),有時(shí)允許一個(gè)參數(shù)可以是任何的類(lèi)型鹏漆,同時(shí)為其他參數(shù)確定某種特定類(lèi)型的能力會(huì)顯得尤為重要巩梢。

public class UnBoundedWildcards2 {
    static Map map1;
    static Map<?, ?> map2;
    static Map<String, ?> map3;

    static void assign1(Map map) {
        map1 = map;
    }

    static void assign2(Map<?, ?> map) {
        map2 = map;
    }

    static void assign3(Map<String, ?> map) {
        map3 = map;
    }

    public static void main(String[] args) {
        assign1(new HashMap());
        assign2(new HashMap());
        // warning
        // Unchecked assignment: 'java.util.HashMap' to 'java.util.Map<java.lang.String,?>'
        assign3(new HashMap());
        
        assign1(new HashMap<String, Integer>());
        assign2(new HashMap<String, Integer>());
        assign3(new HashMap<String, Integer>());
    }
}

但是,當(dāng)擁有的全都是無(wú)界通配符的時(shí)候艺玲,就像在Map<?,?>中看到的那樣括蝠,編譯器看起來(lái)無(wú)法將其與原生Map區(qū)分開(kāi)了。而另外UnBoundedWildcards.java中也展示了編譯器處理List<?>List<? extends Object>時(shí)是不同的饭聚。
而令人困惑的是忌警,編譯器并非總是關(guān)注像ListList<?>之間的差異,因此它們看起來(lái)就像是相同的事物秒梳。事實(shí)上法绵,由于泛型參數(shù)將擦除到它的第一個(gè)邊界箕速,因此List<?>看起來(lái)就等價(jià)于List<Object>,而List實(shí)際上也是List<Object>礼烈。List實(shí)際上是表示“持有任何Object類(lèi)型的原生List”弧满,而List<?>表示“具有某種特定類(lèi)型”的非原生List婆跑,并且我們不知道哪個(gè)類(lèi)型是什么此熬。
那么問(wèn)題來(lái)了,編譯器什么時(shí)候才會(huì)去關(guān)注原生類(lèi)型和涉及無(wú)界通配符的類(lèi)型之間的差異呢滑进?下面的示例使用了之前定義的Holder<T>類(lèi)犀忱,它包含接受Holder作為參數(shù)的各種方法,但是它們具有不同的形式扶关,作為原生類(lèi)型阴汇,具有具體的類(lèi)型參數(shù)以及具有無(wú)界通配符參數(shù)。

public class Wildcards {

    static void rawArgs(Holder holder, Object arg) {
        // unchecked warning
        // holder.set(arg);
        // same warning
        // holder.set(new Wildcards());

        // compile error, don't have any T
        // T t = holder.get();

        // No warning, but type information has been lost
        Object obj = holder.get();
    }


    // 和rawArgs相似节槐,但是其中的warning會(huì)變成編譯錯(cuò)誤
    static void unboundedArg(Holder<?> holder, Object arg) {
        // Compile Error set(capture of ?) but accept Object
        // holder.set(arg);
        // same error
        // holder.set(new Wildcards());

        // compile error, don't have any T
        // T t = holder.get();

        // No warning, but type information has been lost
        Object obj = holder.get();
    }

    static <T> T exact1(Holder<T> holder) {
        T t = holder.get();
        return t;
    }

    static <T> T exact2(Holder<T> holder, T arg) {
        holder.set(arg);
        T t = holder.get();
        return t;
    }

    static <T> T wildSubtype(Holder<? extends T> holder, T arg) {
        // Error
        // set (capture<? extends T>) in Holder cannot be applied to (T)
        // holder.set(arg);
        T t = holder.get();
        return t;
    }

    static <T> void wildSupertype(Holder<? super T> holder, T arg) {
        holder.set(arg);
        // Error
        // Required: T Found: capture<? super T>
        // T t = holder.get();

        // No warning, but type information has been lost
        Object obj = holder.get();
    }

    public static void main(String[] args) {
        Holder raw = new Holder<Long>();
        // Or
        raw = new Holder();
        Holder<Long> qualified = new Holder<>();
        Holder<?> unbounded = new Holder<Long>();
        Holder<? extends Long> bounded = new Holder<Long>();
        Long lng = 1L;

        rawArgs(raw, lng);
        rawArgs(qualified, lng);
        rawArgs(unbounded, lng);
        rawArgs(bounded, lng);

        unboundedArg(raw, lng);
        unboundedArg(qualified, lng);
        unboundedArg(unbounded, lng);
        unboundedArg(bounded, lng);

        // unchecked warning
        Object r1 = exact1(raw);
        Long r2 = exact1(qualified);
        Object r3 = exact1(unbounded);
        Long r4 = exact1(bounded);

        // unchecked warning
        Long r5 = exact2(raw, lng);
        Long r6 = exact2(qualified, lng);
        // Error 參數(shù)錯(cuò)誤
        // Long r7 = exact2(unbounded, lng);
        // Error 參數(shù)錯(cuò)誤
        // Long r8 = exact2(bounded, lng);

        // unchecked warning
        Long r9 = wildSubtype(raw, lng);
        Long r10 = wildSubtype(qualified, lng);
        Object r11 = wildSubtype(unbounded, lng);
        Long r12 = wildSubtype(bounded, lng);

        // unchecked warning
        wildSupertype(raw, lng);
        wildSupertype(qualified,lng);
        // Error 參數(shù)錯(cuò)誤
        // wildSupertype(unbounded,lng);
        // wildSupertype(bounded,lng);
    }
}

rawArgs()中搀庶,編譯器知道Holder是一種泛型類(lèi)型,因此即使它在這里被表示為一個(gè)原生類(lèi)型铜异,編譯器仍舊知道向set()傳遞一個(gè)Object是不安全的哥倔。由于它是原生類(lèi)型,可以將任何類(lèi)型的對(duì)象傳遞給set()揍庄,而這個(gè)對(duì)象將向上轉(zhuǎn)型為Object咆蒿。因此,無(wú)論何時(shí)蚂子,只要使用了原生類(lèi)型沃测,都會(huì)放棄編譯器檢查。而對(duì)get()的調(diào)用說(shuō)明了相同的問(wèn)題:沒(méi)有T類(lèi)型的對(duì)象食茎,因此結(jié)果只能是一個(gè)Object蒂破。
人們很自然地會(huì)開(kāi)始考慮原生HolderHolder<?>是大致相同的事物。但是uboundedArg()強(qiáng)調(diào)它們是不同的——它揭示了相同的問(wèn)題别渔,但是它將這些問(wèn)題作為錯(cuò)誤而不是警告報(bào)告附迷,因?yàn)樵?code>Holder將持有任何類(lèi)型的組合各薇,而Holder<?>將持有某種具體類(lèi)型的同構(gòu)集合饮睬,因此不能只是向其中傳遞Object呕屎。
exact1()exact2()中坏瞄,都使用了確切的泛型參數(shù)——沒(méi)有任何的通配符窗市。而exact2()exact1()多了一個(gè)額外的參數(shù)脆荷。
wildSubType()中蚌成,在Holder類(lèi)型上的限制被放松為持有任何擴(kuò)展至T對(duì)象的Holder芭析,這還是意味著如果TFruit截珍,那么holder可以是Holder<Apple>攀甚。而為了防止將Orange放置到Holder<Apple>中箩朴,對(duì)set()的調(diào)用(或者對(duì)任何接受這個(gè)類(lèi)型參數(shù)為參數(shù)的方法的調(diào)用)都是不允許的。但是秋度,你仍然知道任何來(lái)自Holder<? extends Fruit>的對(duì)象至少是Fruit炸庞,因此get()(或者任何將產(chǎn)生具有這個(gè)類(lèi)型參數(shù)的返回值的方法)都是允許的。
wildSupertype()展示了超類(lèi)型通配符荚斯,這個(gè)方法展示了和wildSubtype()相反的行為:holder可以是持有任何T的基類(lèi)型的容器埠居,所以,set可以接受T事期,因?yàn)榭梢怨ぷ饔诨?lèi)的對(duì)象都可以多態(tài)地作于導(dǎo)出類(lèi)(這里指的是T)滥壕。但是,嘗試著調(diào)用get()是沒(méi)有用的兽泣,因?yàn)橛?code>holder持有的類(lèi)型是任何超類(lèi)型绎橘,唯一安全的是Object
這個(gè)示例還展示了對(duì)于unbounded()中使用無(wú)界通配符能夠做不能上嗎做什么所做出的限制唠倦。對(duì)于遷移兼容性称鳞,rawArgs()將接受所有Holder的不同變體,而不會(huì)產(chǎn)生警告稠鼻。unboundedArg()方法也可以接受相同的所有類(lèi)型冈止,盡管如前所述,它在方法體內(nèi)部處理這些類(lèi)型的防暑并不相同枷餐。
如果向接受“確切”泛型類(lèi)型(沒(méi)有通配符)的方法傳遞一個(gè)原生Holder引用靶瘸,就會(huì)得到一個(gè)警告,因?yàn)榇_切的參數(shù)期望得到在原生類(lèi)型中并不存在的信息毛肋。如果向exact1()傳遞一個(gè)一個(gè)無(wú)界引用怨咪,就不會(huì)有任何可以確定返回類(lèi)型的信息。
而可以看到润匙,exact2()具有最多的限制诗眨,因?yàn)樗M_地得到一個(gè)Holder<T>,以及一個(gè)具有類(lèi)型T的參數(shù)孕讳,正因?yàn)槿绱私吵鼘a(chǎn)生錯(cuò)誤或者警告,除非提供確切的參數(shù)厂财。雖然有時(shí)這樣做很好芋簿,但是如果它過(guò)于受限,那么就可以使用通配符璃饱,這取決于是否想從泛型參數(shù)中返回類(lèi)型確定的返回值(就像在wildSubtype中看到的那樣)与斤,或者是否想要向泛型參數(shù)傳遞類(lèi)型確定的參數(shù)(就像在wildSupertype()中看到那樣)。
因此,使用確切類(lèi)型來(lái)替代通配符的好處是撩穿,可以用泛型參數(shù)來(lái)做更多的事磷支,但是使用通配符使得必須接受范圍更寬的參數(shù)化類(lèi)型作為參數(shù)。因此必須權(quán)衡利弊食寡,找到適合的方法雾狈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市抵皱,隨后出現(xiàn)的幾起案子善榛,更是在濱河造成了極大的恐慌,老刑警劉巖叨叙,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锭弊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡擂错,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)樱蛤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)钮呀,“玉大人,你說(shuō)我怎么就攤上這事昨凡∷祝” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵便脊,是天一觀(guān)的道長(zhǎng)蚂四。 經(jīng)常有香客問(wèn)我,道長(zhǎng)哪痰,這世上最難降的妖魔是什么遂赠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮晌杰,結(jié)果婚禮上跷睦,老公的妹妹穿的比我還像新娘。我一直安慰自己肋演,他們只是感情好抑诸,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著爹殊,像睡著了一般蜕乡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上梗夸,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天层玲,我揣著相機(jī)與錄音,去河邊找鬼。 笑死称簿,一個(gè)胖子當(dāng)著我的面吹牛扣癣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播憨降,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼父虑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了授药?” 一聲冷哼從身側(cè)響起士嚎,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎悔叽,沒(méi)想到半個(gè)月后莱衩,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡娇澎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年笨蚁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趟庄。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡括细,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出戚啥,到底是詐尸還是另有隱情奋单,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布猫十,位于F島的核電站览濒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拖云。R本人自食惡果不足惜贷笛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望江兢。 院中可真熱鬧昨忆,春花似錦、人聲如沸杉允。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)叔磷。三九已至拢驾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間改基,已是汗流浹背繁疤。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人稠腊。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓躁染,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親架忌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吞彤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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

  • 通配符 首先,要展示數(shù)組的一種特殊行為叹放,可以向?qū)С鲱?lèi)型的數(shù)組賦予基類(lèi)型的數(shù)組引用饰恕。 main()的第一行創(chuàng)建了一個(gè)...
    呆呆李宇杰閱讀 389評(píng)論 0 0
  • 2.簡(jiǎn)單泛型 -********Java泛型的核心概念:告訴編譯器想使用什么類(lèi)型, 然后編譯器幫你處理一切細(xì)節(jié) 2...
    CodingHou閱讀 388評(píng)論 0 0
  • 前言 人生苦多,快來(lái) Kotlin 井仰,快速學(xué)習(xí)Kotlin埋嵌! 什么是Kotlin? Kotlin 是種靜態(tài)類(lèi)型編程...
    任半生囂狂閱讀 26,145評(píng)論 9 118
  • 1. 泛型概述 泛型為JDK1.5之后sun公司推出的新功能俱恶,泛型可以消除源代碼中的許多強(qiáng)制類(lèi)型轉(zhuǎn)換雹嗦,泛型對(duì)于數(shù)據(jù)...
    yuan_dongj閱讀 4,484評(píng)論 0 8
  • 昨天和小祖宗聊天,她說(shuō)她找不到她真正開(kāi)懷大笑的照片了速那。我翻開(kāi)空間那個(gè)“僅對(duì)自己可見(jiàn)”的相冊(cè)俐银,看了許久,自從有自拍以...
    素晨Viory閱讀 441評(píng)論 2 1