亚洲三级网站,日本黄色网站在线观看,成年免费网站在线入口,国产一级 片内射新月直播磨磨

        湖北企業(yè)新聞網(wǎng),歡迎您!

        幫助中心 廣告聯(lián)系

        網(wǎng)站關(guān)鍵詞: 湖北企業(yè)新聞網(wǎng)

        Java 中的 T,E,K,V, 別傻傻分不清楚

        來源:時間:2020-09-04 04:09:47 閱讀:-

        前言

        Java 泛型(generics)是 JDK 5 中引入的一個新特性, 泛型提供了編譯時類型安全檢測機制,該機制允許開發(fā)者在編譯時檢測到非法的類型。

        泛型的本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個參數(shù)。

        泛型帶來的好處

        在沒有泛型的情況的下,通過對類型 Object 的引用來實現(xiàn)參數(shù)的“任意化”,“任意化”帶來的缺點是要做顯式的強制類型轉(zhuǎn)換,而這種轉(zhuǎn)換是要求開發(fā)者對實際參數(shù)類型可以預(yù)知的情況下進行的。

        對于強制類型轉(zhuǎn)換錯誤的情況,編譯器可能不提示錯誤,在運行的時候才出現(xiàn)異常,這是本身就是一個安全隱患。

        那么泛型的好處就是在編譯的時候能夠檢查類型安全,并且所有的強制轉(zhuǎn)換都是自動和隱式的。

        public

        class

        GlmapperGeneric
        {

        private
        T t;

        public

        void
        set(T t) {
        this
        .t = t; }

        public
        T get() {
        return
        t; }

        public

        static

        void
        main(
        String
        [] args) {

        // do nothing
        }

        /**
        * 不指定類型
        */

        public

        void
        noSpecifyType(){

        GlmapperGeneric
        glmapperGeneric =
        new

        GlmapperGeneric
        ();
        glmapperGeneric.set(
        "test"
        );

        // 需要強制類型轉(zhuǎn)換

        String
        test = (
        String
        ) glmapperGeneric.get();

        System
        .out.println(test);
        }

        /**
        * 指定類型
        */

        public

        void
        specifyType(){

        GlmapperGeneric
        <
        String
        > glmapperGeneric =
        new

        GlmapperGeneric
        ();
        glmapperGeneric.set(
        "test"
        );

        // 不需要強制類型轉(zhuǎn)換

        String
        test = glmapperGeneric.get();

        System
        .out.println(test);
        }
        }

        上面這段代碼中的 specifyType 方法中 省去了強制轉(zhuǎn)換,可以在編譯時候檢查類型安全,可以用在類,方法,接口上。

        泛型中通配符

        我們在定義泛型類,泛型方法,泛型接口的時候經(jīng)常會碰見很多不同的通配符,比如 T,E,K,V 等等,這些通配符又都是什么意思呢?

        常用的 T,E,K,V,?

        本質(zhì)上這些個都是通配符,沒啥區(qū)別,只不過是編碼時的一種約定俗成的東西。比如上述代碼中的 T ,我們可以換成 A-Z 之間的任何一個 字母都可以,并不會影響程序的正常運行,但是如果換成其他的字母代替 T ,在可讀性上可能會弱一些。通常情況下,T,E,K,V,?是這樣約定的:

        • ?表示不確定的 java 類型
        • T (type) 表示具體的一個java類型
        • K V (key value) 分別代表java鍵值中的Key Value
        • E (element) 代表Element

        ? 無界通配符

        下面先從一個小例子看起:

        我有一個父類 Animal 和幾個子類,如狗、貓等,現(xiàn)在我需要一個動物的列表,我的第一個想法是像這樣的:

        List
        <
        Animal
        > listAnimals

        但是老板的想法確實這樣的:

        List
        extends

        Animal
        > listAnimals

        為什么要使用通配符而不是簡單的泛型呢?通配符其實在聲明局部變量時是沒有什么意義的,但是當(dāng)你為一個方法聲明一個參數(shù)時,它是非常重要的。

        static

        int
        countLegs (List animals ) {

        int retVal = 0;

        for ( Animal animal : animals )
        {
        retVal += animal.countLegs();
        }

        return retVal;
        }
        static

        int
        countLegs1 (
        List< Animal > animals ){
        int retVal = 0;
        for ( Animal animal : animals ) {
        retVal += animal.countLegs();
        }

        return retVal;
        }
        public static void main(String [] args) {
        List dogs = new
        ArrayList<>();

        // 不會報錯
        countLegs( dogs );

        // 報錯
        countLegs1(dogs);
        }

        當(dāng)調(diào)用 countLegs1 時,就會飄紅,提示的錯誤信息如下:

        Java 中的 T,E,K,V, 別傻傻分不清楚

        所以,對于不確定或者不關(guān)心實際要操作的類型,可以使用無限制通配符(尖括號里一個問號,即 ),表示可以持有任何類型。像 countLegs 方法中,限定了上界,但是不關(guān)心具體類型是什么,所以對于傳入的 Animal 的所有子類都可以支持,并且不會報錯。而 countLegs1 就不行。

        上界通配符 < ? extends E>

        上屆:用 extends 關(guān)鍵字聲明,表示參數(shù)化的類型可能是所指定的類型,或者是此類型的子類。

        在類型參數(shù)中使用 extends 表示這個泛型中的參數(shù)必須是 E 或者 E 的子類,這樣有兩個好處:

        • 如果傳入的類型不是 E 或者 E 的子類,編譯不成功
        • 泛型中可以使用 E 的方法,要不然還得強轉(zhuǎn)成 E 才能使用
        private
        extends
        A, E
        extends
        B> E test(K arg1, E arg2){
        E result = arg2;
        arg2.compareTo(arg1);

        //.....

        return
        result;
        }

        類型參數(shù)列表中如果有多個類型參數(shù)上限,用逗號分開

        下界通配符 < ? super E>

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

        在類型參數(shù)中使用 super 表示這個泛型中的參數(shù)必須是 E 或者 E 的父類。

        private

        void
        test(
        List
        super
        T> dst,
        List
        src){

        for
        (T t : src) {
        dst.add(t);
        }
        }
        public

        static

        void
        main(
        String
        [] args) {

        List
        <
        Dog
        > dogs =
        new

        ArrayList
        <>();

        List
        <
        Animal
        > animals =
        new

        ArrayList
        <>();

        new

        Test3
        ().test(animals,dogs);
        }
        // Dog 是 Animal 的子類
        class

        Dog

        extends

        Animal
        {
        }

        dst 類型 “大于等于” src 的類型,這里的“大于等于”是指 dst 表示的范圍比 src 要大,因此裝得下 dst 的容器也就能裝 src 。

        ?和 T 的區(qū)別

        Java 中的 T,E,K,V, 別傻傻分不清楚

        ?和 T 都表示不確定的類型,區(qū)別在于我們可以對 T 進行操作,但是對 ?不行,比如如下這種 :

        // 可以
        T t = operate();
        // 不可以
        ?car = operate();

        簡單總結(jié)下:

        T 是一個 確定的 類型,通常用于泛型類和泛型方法的定義,?是一個 不確定 的類型,通常用于泛型方法的調(diào)用代碼和形參,不能用于定義類和泛型方法。

        區(qū)別1:通過 T 來 確保 泛型參數(shù)的一致性

        // 通過 T 來 確保 泛型參數(shù)的一致性
        public
        extends

        Number
        >
        void
        test(
        List
        dest,
        List
        src)
        //通配符是 不確定的,所以這個方法不能保證兩個 List 具有相同的元素類型
        public

        void
        test(
        List
        extends

        Number
        > dest,
        List
        extends

        Number
        > src)

        像下面的代碼中,約定的 T 是 Number 的子類才可以,但是申明時是用的 String ,所以就會飄紅報錯。

        Java 中的 T,E,K,V, 別傻傻分不清楚

        不能保證兩個 List 具有相同的元素類型的情況

        GlmapperGeneric
        glmapperGeneric = new

        GlmapperGeneric
        <>();
        List

        dest = new

        ArrayList
        <>();
        List
        src = new
        ArrayList<>();
        glmapperGeneric.testNon(dest,src);

        上面的代碼在編譯器并不會報錯,但是當(dāng)進入到 testNon 方法內(nèi)部操作時(比如賦值),對于 dest 和 src 而言,就還是需要進行類型轉(zhuǎn)換。

        區(qū)別2:類型參數(shù)可以多重限定而通配符不行

        Java 中的 T,E,K,V, 別傻傻分不清楚

        使用 & 符號設(shè)定多重邊界(Multi Bounds),指定泛型類型 T 必須是 MultiLimitInterfaceA 和 MultiLimitInterfaceB 的共有子類型,此時變量 t 就具有了所有限定的方法和屬性。對于通配符來說,因為它不是一個確定的類型,所以不能進行多重限定。

        區(qū)別3:通配符可以使用超類限定而類型參數(shù)不行

        類型參數(shù) T 只具有 一種 類型限定方式:

        T extends A

        但是通配符 ? 可以進行 兩種限定:

        ? extends A
        ? super A


        Class 和 Class 區(qū)別

        前面介紹了 ?和 T 的區(qū)別,那么對于, Class 又有什么區(qū)別呢?Class 和 Class

        最常見的是在反射場景下的使用,這里以用一段反射的代碼來說明下。

        // 通過反射的方式生成 multiLimit
        // 對象,這里比較明顯的是,我們需要使用強制類型轉(zhuǎn)換
        MultiLimit
        multiLimit = (
        MultiLimit
        )
        Class
        .forName(
        "com.glmapper.bridge.boot.generic.MultiLimit"
        ).newInstance();

        對于上述代碼,在運行期,如果反射的類型不是 MultiLimit 類,那么一定會報 java.lang.ClassCastException 錯誤。

        對于這種情況,則可以使用下面的代碼來代替,使得在在編譯期就能直接 檢查到類型的問題:

        Java 中的 T,E,K,V, 別傻傻分不清楚

        Class 在實例化的時候,T 要替換成具體類。Class 它是個通配泛型,? 可以代表任何類型,所以主要用于聲明時的限制情況。比如,我們可以這樣做申明:

        // 可以
        public

        Class
        clazz;
        // 不可以,因為 T 需要指定類型
        public

        Class
        clazzT;

        所以當(dāng)不知道定聲明什么類型的 Class 的時候可以定義一 個Class。

        Java 中的 T,E,K,V, 別傻傻分不清楚

        那如果也想 publicClassclazzT; 這樣的話,就必須讓當(dāng)前的類也指定 T ,

        public

        class

        Test3
        {

        public

        Class
        clazz;

        // 不會報錯

        public

        Class
        clazzT;
        }
        推薦閱讀:品牌天下