溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java泛型是什么

發(fā)布時(shí)間:2021-09-05 11:43:34 來(lái)源:億速云 閱讀:137 作者:小新 欄目:開發(fā)技術(shù)

這篇文章主要介紹了Java泛型是什么,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

    學(xué)習(xí)目標(biāo)

    什么是泛型

    為什么需要泛型

    如何使用泛型

    如何自定義泛型

    類型通配符等知識(shí)

    1. 什么是泛型

    泛型不只是 Java 語(yǔ)言所特有的特性,泛型是程序設(shè)計(jì)語(yǔ)言的一種特性。

    允許程序員在強(qiáng)類型的程序設(shè)計(jì)語(yǔ)言中編寫代碼時(shí)定義一些可變部分,那些部分在使用前必須做出聲明。

    Java 中的集合類是支持泛型的,它在代碼中是這個(gè)樣子的

    Java泛型是什么

    代碼中的<Integer>就是泛型,我們把類型像參數(shù)一樣傳遞,尖括號(hào)中間就是數(shù)據(jù)類型,我們可以稱之為實(shí)際類型參數(shù),這里實(shí)際類型參數(shù)的數(shù)據(jù)類型只能為引用數(shù)據(jù)類型。

    那么為什么需要泛型呢?

    2. 為什么需要泛型

    我們?cè)谑褂?code>ArrayList實(shí)現(xiàn)類的時(shí)候,如果沒有指定泛型,IDEA會(huì)給出警告,代碼似乎也是可以順利運(yùn)行的。請(qǐng)看如下實(shí)例:

    import java.util.ArrayList;
    
    public class testDemo1 {
    
        public static void main(String[] args) {
            ArrayList arrayList = new ArrayList();
            arrayList.add("Hello");
            String str1 = (String) arrayList.get(0);
            System.out.println("str=" + str1);
        }
    
    }

    運(yùn)行結(jié)果:

    str1=Hello

    雖然運(yùn)行時(shí)沒有發(fā)生任何異常,但這樣做有兩個(gè)缺點(diǎn):

    1. 需要強(qiáng)制類型轉(zhuǎn)換: 由于ArrayList內(nèi)部就是一個(gè)Object[]數(shù)組,在get()元素的時(shí)候,返回的是Object類型,所以在ArrayList外獲取該對(duì)象,需要強(qiáng)制類型轉(zhuǎn)換。其它的Collection、Map如果不使用泛型,也存在這個(gè)問(wèn)題;

    2. 可向集合中添加任意類型的對(duì)象,存在類型不安全風(fēng)險(xiǎn)。例如如下代碼中,我們向列表中既添加了Integer類型,又添加了String類型:

    package com.caq.oop.demo08;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Test {
        public static void main(String[] args) {
            //實(shí)例化一個(gè)空列表
            List arrayList = new ArrayList<>();
            arrayList.add(123);
            arrayList.add("sad");
            String str = (String) arrayList.get(0);
    
        }
    }

    Exception in thread “main” java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at com.caq.oop.demo08.Test.main(Test.java:12)

    由于我們的“疏忽”,列表第 1 個(gè)元素實(shí)際上是整型,但被我們強(qiáng)制轉(zhuǎn)換為字符串類型,這是行不通的,因此會(huì)拋出ClassCastException異常。

    使用泛型可以解決這些問(wèn)題。泛型有如下優(yōu)點(diǎn):

    1. 可以減少類型轉(zhuǎn)換的次數(shù),代碼更加簡(jiǎn)潔;

    2. 程序更加健壯:只要編譯期沒有警告,運(yùn)行期就不會(huì)拋出ClassCastException異常;

    3. 提高了代碼的可讀性:編寫集合的時(shí)候,就限定了集合中能存放的類型。

    3. 如何使用泛型

    3.1 泛型使用

    在代碼中,這樣使用泛型:

    List<String> list = new ArrayList<String>();
    // Java 7 及以后的版本中,構(gòu)造方法中可以省略泛型類型:
    List<String> list = new ArrayList<>();
    外幣巴伯

    要注意的是,變量聲明的類型必須與傳遞給實(shí)際對(duì)象的類型保持一致,下面是錯(cuò)誤的例子:

    List<Object> list = new ArrayList<String>();
    List<Number> numbers = new ArrayList(Integer);

    3.2 自定義泛型類

    3.2.1 Java 源碼中泛型的定義

    在自定義泛型類之前,我們來(lái)看下java.util.ArrayList是如何定義的:

    Java泛型是什么

    類名后面的<E>就是泛型的定義,E不是 Java 中的一個(gè)具體的類型,它是 Java 泛型的通配符(注意是大寫的,實(shí)際上就是Element的含義),可將其理解為一個(gè)占位符,將其定義在類上,使用時(shí)才確定類型。

    此處的命名不受限制,但最好有一定含義,例如java.lang.HashMap的泛型定義為HashMap<K,V>K表示Key,V表示Value。

    3.2.2 自定義泛型類實(shí)例1

    下面我們來(lái)自定義一個(gè)泛型類,自定義泛型按照約定俗成可以叫<T>,具有Type的含義,實(shí)例如下:

    實(shí)例演示

    package com.caq.List;
    
    public class Generic01<T> {
    
        private T abc;//定義在類上的泛型,在類內(nèi)部可以使用
    
        public T getAbc() {
            return abc;
        }
    
        public void setAbc(T abc) {
            this.abc = abc;
        }
    
        public static void main(String[] args) {
            //實(shí)例化對(duì)象,指定元素類型為整型
            Generic01<Integer> integerGeneric01 = new Generic01<>();
            //調(diào)用方法
            integerGeneric01.setAbc(100);
            System.out.println("integerGeneric01="+ integerGeneric01.getAbc());
    
            //實(shí)例化對(duì)象,指定元素類型為長(zhǎng)類型
            Generic01<Long> longGeneric01 = new Generic01<>();
            longGeneric01.setAbc(200L);
            System.out.println("longGeneric01="+ longGeneric01.getAbc());
    
            // 實(shí)例化對(duì)象,指定元素類型為雙精度浮點(diǎn)型
            Generic01<Double> doubleGeneric01 = new Generic01<>();
            doubleGeneric01.setAbc(300.0);
            System.out.println("doubleGeneric01="+ doubleGeneric01.getAbc());
            
        }
    }

    運(yùn)行結(jié)果:

    integerGeneric01=100
    longGeneric01=200
    doubleGeneric01=300.0

    我們?cè)陬惖亩x處也定義了泛型:Generic01<T>;在類內(nèi)部定義了一個(gè)T類型的abc變量,并且為其添加了settergetter方法。

    解釋:對(duì)于泛型類的使用也很簡(jiǎn)單,在主方法中,創(chuàng)建對(duì)象的時(shí)候指定T的類型分別為Integer、Long、Double,類就可以自動(dòng)轉(zhuǎn)換成對(duì)應(yīng)的類型了。

    3.2.3 自定義泛型類實(shí)例2

    上面我們知道了如何定義含有單個(gè)泛型的類,那么對(duì)于含有多個(gè)泛型的類,如何定義呢?

    我們可以看一下HashMap類是如何定義的。如下是 Java 源碼的截圖:

    Java泛型是什么

    參照HashMap<K,V>類的定義,下面我們來(lái)看看如何定義含有兩個(gè)泛型的類

    package com.caq.List;
    
    public class Generic02<K, V> {//這次是定義兩個(gè)泛型在類上
    
        //定義類型為K的key屬型
        private K key;
    
        //定義類型為V的value屬型
        private V value;
    
        //封裝里的知識(shí),通過(guò)Getter和Setter方法來(lái)設(shè)置和獲取私有屬型的值
        public K getKey() {
            return key;
        }
    
        public void setKey(K key) {
            this.key = key;
        }
    
        public V getValue() {
            return value;
        }
    
        public void setValue(V value) {
            this.value = value;
        }
    
        public static void main(String[] args) {
            //實(shí)例化對(duì)象,分別指定類型為整型,長(zhǎng)整型
            Generic02<Integer, Long> integerLongGeneric02 = new Generic02<>();
            //實(shí)例化對(duì)象,分別指定類型為浮點(diǎn)型、字符串類型
            Generic02<Float, String> floatStringGeneric02 = new Generic02<>();
    
            integerLongGeneric02.setKey(100);
            integerLongGeneric02.setValue(200L);
            System.out.println("key=" + integerLongGeneric02.getKey());
            System.out.println("value=" + integerLongGeneric02.getValue());
    
            floatStringGeneric02.setKey(0.9f);
            floatStringGeneric02.setValue("巴啦啦能量");
            System.out.println("key=" + floatStringGeneric02.getKey());
            System.out.println("value=" + floatStringGeneric02.getValue());
        }
    }

    運(yùn)行結(jié)果:

    key=100value=200key=0.9value=巴啦啦能量

    3.3 自定義泛型方法

    前面我們知道了如何定義泛型類,在類上定義的泛型,在方法中也可以使用。下面我們來(lái)看一下如何自定義泛型方法。

    泛型方法不一定寫在泛型類當(dāng)中。當(dāng)類的調(diào)用者總是關(guān)心類中的某個(gè)泛型方法,不關(guān)心其他屬性,這個(gè)時(shí)候就沒必要再整個(gè)類上定義泛型了。

    直接在方法上設(shè)置泛型(generic)

    package com.caq.List;public class Generic03 {        public <T> void test(T t){        System.out.println(t);    }    public static void main(String[] args) {        Generic03 generic03 = new Generic03();                generic03.test("Monkey");        generic03.test(1);        generic03.test(1.00000);        generic03.test(1L);    }}

    運(yùn)行結(jié)果:

    Monkey11.01

    實(shí)例中,使用<T>來(lái)定義test方法的泛型,它接收一個(gè)泛型的參數(shù)變量并在方法體打?。徽{(diào)用泛型方法也很簡(jiǎn)單,在主方法中實(shí)例化對(duì)象,調(diào)用對(duì)象下的泛型方法,可傳入不同類型的參數(shù)。

    4. 泛型類的子類

    泛型類也是一個(gè) Java 類,它也具有繼承的特性。

    泛型類的繼承可分為兩種情況:

    1. 子類明確泛型類的類型參數(shù)變量;

    2. 子類不明確泛型類的類型參數(shù)變量。

    4.1 明確類型參數(shù)變量

    例如,有一個(gè)泛型接口:

    package com.caq.List;public interface GenericInterface01<T> {    default void show(T t) {            }}

    泛型接口的實(shí)現(xiàn)類如下:

    package com.caq.List;public class GenericInterfaceImple implements GenericInterface01<String> {    @Override    public void show(String s) {        System.out.println(s);    }}

    子類實(shí)現(xiàn)明確了泛型的參數(shù)變量為String類型。因此方法show()的重寫也將T替換為了String類型。

    4.2 不明確類型參數(shù)變量

    當(dāng)實(shí)現(xiàn)類不確定泛型類的參數(shù)變量時(shí),實(shí)現(xiàn)類需要定義類型參數(shù)變量,調(diào)用者使用子類時(shí),也需要傳遞類型參數(shù)變量。

    如下是GenericInterface接口的另一個(gè)實(shí)現(xiàn)類:

    package com.caq.List;
    
    public class GenericInterfaceImple<T> implements GenericInterface01<T> {
        @Override
        public void show(T t) {
            System.out.println(t);
        }
    }

    在主方法中調(diào)用實(shí)現(xiàn)類的show()方法:

    package com.caq.List;
    
    public class GenericInterfaceImple<T> implements GenericInterface01<T> {
        @Override
        public void show(T t) {
            System.out.println(t);
        }
    
        public static void main(String[] args) {
            GenericInterfaceImple<Integer> integerGenericInterfaceImple = new GenericInterfaceImple<>();
            integerGenericInterfaceImple.show(100);
        }
    }
    
    
    100

    5. 類型通配符

    我們先來(lái)看一個(gè)泛型作為方法參數(shù)的實(shí)例:

    package com.caq.List;
    /**
     * 遍歷并打印集合中的每一個(gè)元素
     * 遍歷是二叉樹上最重要的運(yùn)算之一,是二叉樹上進(jìn)行其它運(yùn)算之基礎(chǔ)。 樹的遍歷是樹的一種重要的運(yùn)算。 
     * 所謂遍歷是指對(duì)樹中所有結(jié)點(diǎn)的信息的訪問(wèn),即依次對(duì)樹中每個(gè)結(jié)點(diǎn)訪問(wèn)一次且僅訪問(wèn)一次。
    
     * @param list 要接收的集合
     */
    public class Generic04 {
        public void printListElement(List<object> list){
            for (Object o :
                    list) {
                System.out.println(o);
            }
        }
    }

    觀察上面的代碼,參數(shù)list的限定的泛型類型為Object, 也就是說(shuō),這個(gè)方法只能接收元素為Object類型的集合,如果我們想傳遞其他元素類型的集合,是行不通的。例如,如果傳遞裝載Integer元素的集合,程序在編譯階段就會(huì)報(bào)錯(cuò):

    Java泛型是什么

    Tips: 泛型中的List<Object>并不是List<Integer>的父類,它們不滿足繼承關(guān)系。

    5.1 無(wú)限定通配符

    想要解決這個(gè)問(wèn)題,使用類型通配符即可,修改方法參數(shù)處的代碼,將<>中間的Object改為?即可:

    public void printListElement(List<?> list){

    此處的?就是類型通配符,表示可以匹配任意類型,因此調(diào)用方可以傳遞任意泛型類型的列表。

    實(shí)例演示

    package com.caq.List;
    
    import java.util.ArrayList;
    import java.util.List;
    
    
    public class Generic04 {
    //List<?>可以理解為列表的類型,可以是整數(shù)型列表,也可以是字符串類型列表,list代表的是遍歷的元素代表
        public void printListElement(List<?> list){
            for (Object o : list) {
                System.out.println(o);
            }
        }
    
        public static void main(String[] args) {
            //實(shí)例化一個(gè)整型列表
            List<Integer> interger = new ArrayList<>();
            //加元素
            interger.add(1);
            interger.add(2);
            interger.add(2222);
    
            //實(shí)例化對(duì)象
            Generic04 generic04 = new Generic04();
            generic04.printListElement(interger);
    
            //實(shí)例化一個(gè)字符串類型列表
            ArrayList<String> strings = new ArrayList<>();
            strings.add("element1");
            strings.add("element2");
            strings.add("element3");
    
            generic04.printListElement(strings);
        }
    }

    運(yùn)行結(jié)果:

    1
    2
    2222
    element1
    element2
    element3

    5.2 extends 通配符

    extends通配符用來(lái)限定泛型的上限。什么意思呢?依舊以上面的實(shí)例為例,我們來(lái)看一個(gè)新的需求,我們希望方法接收的List 集合限定在數(shù)值類型內(nèi)(float、integer、double、byte 等),不希望其他類型可以傳入(比如字符串)。此時(shí),可以改寫上面的方法定義,設(shè)定上界通配符:

    public void printListElement(List<? extends Number> list) {

    這樣的寫法的含義為:List集合裝載的元素只能是Number自身或其子類(Number類型是所有數(shù)值類型的父類),完整實(shí)例如下:

    import java.util.ArrayList;
    import java.util.List;
    
    public class Generic04 {
    
        public void printListElement(List<? extends Number> list) {
            for (Object o : list) {
                System.out.println(o);
            }
        }
    
        public static void main(String[] args) {
            // 實(shí)例化一個(gè)整型的列表
            List<Integer> integers = new ArrayList<>();
            // 添加元素
            integers.add(1);
            integers.add(2);
            integers.add(3);
            GenericDemo4 generic04 = new Generic04();
            // 調(diào)用printListElement()方法
            generic04.printListElement(integers);
    
        }
    }

    運(yùn)行結(jié)果:

    1
    2
    3

    5.3 super 通配符

    既然已經(jīng)了解了如何設(shè)定通配符上界,也就不難理解通配符的下界了,可以限定傳遞的參數(shù)只能是某個(gè)類型的父類。

    語(yǔ)法如下:

    <? super Type>

    感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Java泛型是什么”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI