成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

Java泛型的重要目的:別讓貓別站在狗隊(duì)里

《Java編程思想》第四版足足用了75頁來講泛型——厚厚的一沓內(nèi)容,很容易讓人頭大——但其實(shí)根本不用這么多,只需要一句話:我是一個(gè)泛型隊(duì)列,狗可以站進(jìn)來,貓也可以站進(jìn)來,但最好不要既站貓,又站狗!

清苑網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、響應(yīng)式網(wǎng)站設(shè)計(jì)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營維護(hù)。創(chuàng)新互聯(lián)從2013年開始到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。

01、泛型是什么

泛型,有人拆解這個(gè)詞為“參數(shù)化類型”。這種拆解其實(shí)也不好理解,還是按照沉默王二的意思來理解一下吧。

現(xiàn)在有一只玻璃杯,你可以讓它盛一杯白開水,也可以盛一杯二鍋頭——泛型的概念就在于此,制造這只杯子的時(shí)候沒必要在說明書上定義死,指明它只能盛白開水而不能盛二鍋頭!

可以在說明書上指明它用來盛裝液體,但最好也不要這樣,弄不好用戶想用它來盛幾塊冰糖呢!

這么一說,你是不是感覺不那么抽象了?泛型其實(shí)就是在定義類、接口、方法的時(shí)候不局限地指定某一種特定類型,而讓類、接口、方法的調(diào)用者來決定具體使用哪一種類型的參數(shù)。

就好比,玻璃杯的制造者說,我不知道使用者用這只玻璃杯來干嘛,所以我只負(fù)責(zé)造這么一只杯子;玻璃杯的使用者說,這就對(duì)了,我來決定這只玻璃杯是盛白開水還是二鍋頭,或者冰糖。

02、什么時(shí)候用泛型

我們來看一段簡(jiǎn)短的代碼:

public class Cmower {

    class Dog {
    }

    class Cat {
    }

    public static void main(String[] args) {
        Cmower cmower = new Cmower();
        Map map = new HashMap();
        map.put("dog", cmower.new Dog());
        map.put("cat", cmower.new Cat());

        Cat cat = (Cat) map.get("dog");
        System.out.println(cat);
    }

}

這段代碼的意思是:我們?cè)趍ap中放了一只狗(Dog),又放了一只貓(Cat),當(dāng)我們想從map中取出貓的時(shí)候,卻一不留神把狗取了出來。

這段代碼編譯是沒有問題的,但運(yùn)行的時(shí)候就會(huì)報(bào)ClassCastException(狗畢竟不是貓?。?/p>

Exception in thread "main" java.lang.ClassCastException: com.cmower.java_demo.sixteen.Cmower$Dog cannot be cast to com.cmower.java_demo.sixteen.Cmower$Cat
    at com.cmower.java_demo.sixteen.Cmower.main(Cmower.java:20)

為什么會(huì)這樣呢?

1)寫代碼的程序員粗心大意。要從map中把貓取出來,你不能取狗啊!

2)創(chuàng)建map的時(shí)候,沒有明確指定map中要放的類型。如果指定是要放貓,那肯定取的時(shí)候就是貓,不會(huì)取出來狗;如果指定是要放狗,也一個(gè)道理。

第一種情況不太好解決,總不能把程序員打一頓(我可不想做一個(gè)天天背鍋的程序員,很重的好不好);第二種情況就比較容易解決,因?yàn)镸ap支持泛型(泛型接口)。

public interface Map<K,V> {
}

注:在Java中,經(jīng)常用T、E、K、V等形式的參數(shù)來表示泛型參數(shù)。

T:代表一般的任何類。
E:代表 Element 的意思,或者 Exception 異常的意思。
K:代表 Key 的意思。
V:代表 Value 的意思,通常與 K 一起配合使用。

既然Map支持泛型,那作為Map的實(shí)現(xiàn)者HashMap(泛型類)也支持泛型了。

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

}

其中的put方法(泛型方法)是這樣定義的:

public V put(K key, V value) {
  return putVal(hash(key), key, value, false, true);
}

好了,現(xiàn)在使用泛型的形式來定義一個(gè)只能放Cat的Map吧!

public class Cmower {

    class Dog {
    }

    class Cat {
    }

    public static void main(String[] args) {
        Cmower cmower = new Cmower();
        Map<String, Cat> map = new HashMap<>();
//      map.put("dog", cmower.new Dog()); // 不再允許添加
        map.put("cat", cmower.new Cat());

        Cat cat = map.get("cat");
        System.out.println(cat);
    }
}

當(dāng)使用泛型定義map(鍵為String類型,值為Cat類型)后:

1)編譯器就不再允許你向map中添加狗的對(duì)象了。

2)當(dāng)你從map中取出貓的時(shí)候,也不再需要強(qiáng)制轉(zhuǎn)型了。

03、類型擦除

有人說,Java的泛型做的只是表面功夫——泛型信息存在于編譯階段(狗隊(duì)在編譯時(shí)不允許站貓),運(yùn)行階段就消失了(運(yùn)行時(shí)的隊(duì)列里沒有貓的信息,連狗的信息也沒有)——這種現(xiàn)象被稱為“類型擦除”。

來,看代碼解釋一下:

public class Cmower {

    class Dog {
    }

    class Cat {
    }

    public static void main(String[] args) {
        Cmower cmower = new Cmower();
        Map<String, Cat> map = new HashMap<>();
        Map<String, Dog> map1 = new HashMap<>();

        // The method put(String, Cmower.Cat) in the type Map<String,Cmower.Cat> is not applicable for the arguments (String, Cmower.Dog)
        //map.put("dog",cmower.new Dog());

        System.out.println(map.getClass());
        // 輸出:class java.util.HashMap
        System.out.println(map1.getClass());
        // 輸出:class java.util.HashMap
    }

}

map的鍵位上是Cat,所以不允許put一只Dog;否則編譯器會(huì)提醒The method put(String, Cmower.Cat) in the type Map&lt;String,Cmower.Cat&gt; is not applicable for the arguments (String, Cmower.Dog)。編譯器做得不錯(cuò),值得點(diǎn)贊。

但是問題就來了,map的Class類型為HashMap,map1的Class類型也為HashMap——也就是說,Java代碼在運(yùn)行的時(shí)候并不知道m(xù)ap的鍵位上放的是Cat,map1的鍵位上放的是Dog。

那么,試著想一些可怕的事情:既然運(yùn)行時(shí)泛型的信息被擦除了,而反射機(jī)制是在運(yùn)行時(shí)確定類型信息的,那么利用反射機(jī)制,是不是就能夠在鍵位為Cat的Map上放一只Dog呢?

我們不妨來試一試:

public class Cmower {

    class Dog {
    }

    class Cat {
    }

    public static void main(String[] args) {
        Cmower cmower = new Cmower();
        Map<String, Cat> map = new HashMap<>();

        try {
            Method method = map.getClass().getDeclaredMethod("put",Object.class, Object.class);

            method.invoke(map,"dog", cmower.new Dog());

            System.out.println(map);
            // {dog=com.cmower.java_demo.sixteen.Cmower$Dog@55f96302}
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}

看到?jīng)]?我們竟然在鍵位為Cat的Map上放了一只Dog!

注:Java的設(shè)計(jì)者在JDK 1.5時(shí)才引入了泛型,但為了照顧以前設(shè)計(jì)上的缺陷,同時(shí)兼容非泛型的代碼,不得不做出了一個(gè)折中的策略:編譯時(shí)對(duì)泛型要求嚴(yán)格,運(yùn)行時(shí)卻把泛型擦除了——要兼容以前的版本,還要升級(jí)擴(kuò)展新的功能,真的很不容易!

04、泛型通配符

有些時(shí)候,你會(huì)見到這樣一些代碼:

List<? extends Number> list = new ArrayList<>();
List<? super Number> list = new ArrayList<>();

?和關(guān)鍵字extends或者super在一起其實(shí)就是泛型的高級(jí)應(yīng)用:通配符。

我們來自定義一個(gè)泛型類——PetHouse(寵物小屋),它有一些基本的動(dòng)作(可以住進(jìn)來一只寵物,也可以放出去):

public class PetHouse<T> {
    private List<T> list;

    public PetHouse() {
    }

    public void add(T item) {
        list.add(item);
    }

    public T get() {
        return list.get(0);
    }
}

如果我們想要住進(jìn)去一只寵物,可以這樣定義小屋(其泛型為Pet):

PetHouse<Pet> petHouse = new PetHouse<>();

然后,我們讓小貓和小狗住進(jìn)去:

petHouse.add(new Cat());
petHouse.add(new Dog());

如果我們只想要住進(jìn)去一只小貓,打算這樣定義小屋:

PetHouse<Pet> petHouse = new PetHouse<Cat>();

但事實(shí)上,編譯器不允許我們這樣定義:因?yàn)榉盒筒恢苯又С窒蛏限D(zhuǎn)型。該怎么辦呢?

可以這樣定義小屋:

PetHouse<? extends Pet> petHouse = new PetHouse<Cat>();

也就是說,寵物小屋可以住進(jìn)去小貓,但它必須是寵物(Pet或者Pet的子類)而不是一只野貓。

但很遺憾,這個(gè)寵物小屋實(shí)際上住不了小貓,看下圖。

Java泛型的重要目的:別讓貓別站在狗隊(duì)里

這是因?yàn)镴ava雖然支持泛型的向上轉(zhuǎn)型(使用 extends 通配符),但我們卻無法向其中添加任何東西——編譯器并不知道寵物小屋里要住的是小貓還是小狗,或者其他寵物,因此干脆什么都不讓住。

看到這,你一定非常疑惑,既然PetHouse&lt;? extends Pet&gt;定義的寵物小屋什么也不讓住,那為什么還要這樣定義呢?思考一下。

05、讀者將軍的總結(jié)

泛型限定符有一描述:上界不存下界不取。

上界不存的原因:例如 List,編譯器只知道容器內(nèi)是 Father 及其子類,具體是什么類型并不知道,編譯器在看到 extends 后面的 Father 類,只是標(biāo)上一個(gè) CAP#1 作為占位符,無論往里面插什么,編譯器都不知道能不能和 CAP#1 匹配,所以就不允許插入。

extends的作用:可以在初始化的時(shí)候存入一個(gè)值,并且能保證數(shù)據(jù)的穩(wěn)定性,只能取不能存。讀取出來的數(shù)據(jù)可以存在父類或者基類里。

下界不取的原因:下界限定了元素的最小粒度,實(shí)際上是放松了容器元素的類型控制。例如 List, 元素是 Father 的基類,可以存入 Father 及其子類。但編譯器并不知道哪個(gè)是 Father 的超類,如 Human。讀取的時(shí)候,自然不知道是什么類型,只能返回 Object,這樣元素信息就全部丟失了。

super的作用:用于參數(shù)類型限定。

PECS 原則:

1.頻繁往外讀取內(nèi)容的,適合用extends
2.經(jīng)常往里插入的,適合用super


上一篇:Java 數(shù)組,看這篇就夠了

下一篇:HashMap,難的不在Map,而在Hash

微信搜索「沉默王二」公眾號(hào),關(guān)注后回復(fù)「免費(fèi)視頻」獲取 500G 高質(zhì)量教學(xué)視頻(已分門別類)。

新聞名稱:Java泛型的重要目的:別讓貓別站在狗隊(duì)里
URL地址:http://jinyejixie.com/article0/posjoo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供標(biāo)簽優(yōu)化、動(dòng)態(tài)網(wǎng)站、外貿(mào)網(wǎng)站建設(shè)、響應(yīng)式網(wǎng)站網(wǎng)站排名、ChatGPT

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)
文山县| 察隅县| 祁阳县| 巩义市| 台安县| 邵东县| 临桂县| 巧家县| 长顺县| 普格县| 新野县| 横峰县| 宁海县| 嘉义县| 康乐县| 宜章县| 南平市| 新疆| 开阳县| 双流县| 台安县| 望谟县| 义乌市| 兰州市| 盘锦市| 五莲县| 柞水县| 朝阳区| 浦东新区| 富蕴县| 鄂托克前旗| 延边| 贵溪市| 汝城县| 通许县| 沅陵县| 民县| 瑞金市| 突泉县| 江城| 平塘县|