<span id="35v3v"><th id="35v3v"></th></span>
<address id="35v3v"></address>

    <address id="35v3v"></address>
      <address id="35v3v"><listing id="35v3v"><meter id="35v3v"></meter></listing></address>
        <listing id="35v3v"><listing id="35v3v"></listing></listing>

        <listing id="35v3v"><listing id="35v3v"><menuitem id="35v3v"></menuitem></listing></listing>

        <noframes id="35v3v"><noframes id="35v3v">
        當前位置: 首頁 / 技術分享 / 正文
        民哥帶你快速精通java泛型(一)

        2022-10-18

        泛型 類型   

          泛型由入門到精通

          Hi,小伙伴你好~歡迎進入泛型的學習,在學習之前友情提醒一下:學習泛型需要小伙伴們具備一定的javaSE基礎,如果之前小伙伴們沒有接觸過java,大家可以移步到千鋒北京java好程序員的javaSE課程進行學習。

          在正式開始學習之前,我們先來看一段經常書寫的代碼,分析一下代碼存在那些問題?

          代碼如下:

        public class GenericsDemo {
        public static void main(String[] args) {
        //1.創建一個List對象
        List list = new ArrayList();
        //2.向List中添加數據
        list.add("python");
        list.add("java");
        list.add(66);
        //3.遍歷集合
        for (int i = 0; i <list.size() ; i++) {
        //4.把集合中的每個元素轉成String類型
        String ele = (String) list.get(i);
        //5.打印-測試結果
        System.out.println("元素的值:"+ele);
        }
        }
        }

          運行代碼,會報如下圖的異常:

        a1

          那么小伙們,我們來分析一下原因,到底是因為什么報這個異常呢?

          在代碼中我們定義了一個List類型的集合,先向其中加入了兩個String類型的值,隨后加入一個Integer類型的值。這是完全允許的,因為此時list默認的類型為Object類型,所以在代碼編譯期間沒有任何問題。

          但是在運行代碼時,由于list集合中既有String類型的值,又有Integer類型的值,致使list集合無法區分值是什么類型,很容易出現上圖中的錯誤。因為編譯階段正常,而運行時會出現“java.lang.ClassCastException”異常。因此導致此類錯誤編碼過程中不易發現。

          分析完了,小伙們現在明白了吧,通過分析我們發現上述代碼主要存在兩個問題:

          當我們將數據存入集合時,集合不會記住數據的類型,默認數據類型都是Object。

          當我們遍歷集合中的數據時,人為進行強制類型轉換,很容易報“java.lang.ClassCastException”。強制類型轉換異常

          那么有沒有什么辦法可以使集合能夠記住集合內元素各類型,且能夠達到只要編譯時不出現問題,運行時就

          不會出現“java.lang.ClassCastException”異常呢?

          答案就是使用泛型。

          那么什么是泛型呢?

          第一關 讓我們一起走入泛型

          1.泛型的概述

          1.1 什么是泛型

          泛型,泛指任意類型,可以應用在接口上,類上,變量上,方法上,以及方法的參數中。

          百度百科介紹:

          泛型是jdk1.5的新特性,泛型的本質是參數化類型,也就是說所操作的數據類型被指定為一個參數。這種參數類型可以用在類、接口和方法的創建中,分別稱為泛型類、泛型接口、泛型方法。

          在jdk1.5之前,沒有泛型的情況的下,通過對類型Object的引用來實現參數的“任意化”,“任意化”帶來的缺點是要做顯式的強制類型轉換,而這種轉換是要求開發者對實際參數類型可以預知的情況下進行的。對于強制類型轉換錯誤的情況 ,編譯器可能不提示錯誤,在運行的時候才出現異常,這是一個安全隱患。

          泛型的好處:使用泛型,首先可以通過IDE進行代碼類型初步檢查,然后在編譯階段進行編譯類型檢查,以保證類型轉換的安全性;并且所有的強制轉換都是自動和隱式的,可以提高代碼的重用率。

          個人理解:

          簡單來說:泛型,即“參數化類型”,那么類型“參數化”到底怎么理解呢?

          顧名思義,類型“參數化”就是將類型由原來的具體類型,變成參數化的“類型”,有點類似于方法中的變量參數,不過此時是類型定義成參數形式(你可以理解為類型形參),然后在使用時傳入具體的類型(也就是類型實參)。為什么這樣操作呢?因為它能讓類型"參數化",也就是在不創建新的類型的情況下,通過泛型可以指定不同類型來控制形參具體限制的類型。

          總結:

          泛型介紹完了, 小伙伴看完上述解釋后,能理解泛型是什么了嗎?我們可以用兩句話來概括一下:

          泛型在聲明時,用標記符表示,僅僅作為“參數化的類型”,可以理解為形式參數。

          比如:

          //定義List集合時,用標記符E表示任意類型,E可以理解為形式參數,沒有具體的類型值public interface Listextends Collection{ ----------}

          泛型在使用時,需要指定具體的類型,也就是類型實際的參數。

          比如:

          //在使用List集合時,需要確定E的具體類型,String可以理解為具體的類型,也就是實際的參數值Listlist = new ArrayList();

          1.2 常用的泛型標記符

          E - Element (集合使用,因集合中存放元素)

          T - Type(Java 類)

          K - Key(鍵) V - Value(值)

          N - Number(數值類型)

          ? - 表示不確定的java類型

          S、U、V - 2nd、3rd、4th types

          你可能會有疑問,弄這么多標識符干嘛,直接使用萬能的Object難道不香么?我們知道Object是所有類的基類(任何類如果沒有指明其繼承類,都默認繼承于Object類),因此任何類的對象都可以設置Object的引用,只不過在使用的時候可能要類型強制轉換。但是如果設置了泛型E、T等這些標識符,那么在實際使用之前類型就已經確定,因此不再需要類型強制轉換

          2.泛型的語法使用

          2.1 泛型在集合中的使用

          單列集合中List中

        public interface List<E> extends Collection<E> {
        ----
        <T> T[] toArray(T[] a);
        ----
        }

          雙列集合Map中

        public class HashMap<K,V> extends AbstractMap<K,V>
        implements Map<K,V>, Cloneable, Serializable {
        ---
        V get(Object key){---};
        V put(K key, V value){---};
        V remove(Object key){---};
        void putAll(Map<? extends K, ? extends V> m){---};
        -----
        }

          小伙們可以看到:List,HashMap的源碼,在聲明集合時或者定義方法時,使用采用尖括號內加占位符的形式 ,這里的占位符就是我們上面說的泛型標記符,泛型標記符號E,K,V,T等用來表示任意類型(E,K,V,T也就是“泛型形參”,在實例化集合對象時需要明確的具體的類型(也就是“泛型的實際參數”))。

          通過觀察集合的源碼,那么我們自己也可以定義泛型接口,泛型類以及泛型方法,下面我們一起操作一下吧。

          2.2 聲明泛型接口

          泛型應用于接口。例如生成器(GeneratorType),這是一種專門負責創建對象的類。當使用生成器創建新的對象時,它不需要任何參數,也就是說生成器無需額外的信息就知道如何創建新對象。

          一般而言,一個生成器只定義一個方法,該方法用以產生新的對象。

        /**
        * 定義一個泛型接口:生成任意對象
        * @param <T>: 泛型形式參數,可以是任意類型
        */
        public interface GeneratorType<T> {
        T create();
        }

        /**
        * 測試泛型接口
        */
        class DemoGeneratorType{
        public static void main(String[] args) {
        //1.使用生成器:創建random對象
        GeneratorType<Random> gt= new GeneratorType<Random>() {
        @Override
        public Random create() {
        return new Random();
        }
        };
        //2.使用 GeneratorType:創建對象
        Random random = gt.create();
        }
        }

          來,小伙伴們,我們一起分析下上面的代碼:

          我們聲明了一個泛型接口 GeneratorType,目的用來生成任意類型的對象,在這里T可以表示任意類型。

          我們在測試類中,通過GeneratorType創建對象時,可以傳遞任意類型。

          比如 GeneratorType,那么就可以生成Random對象了

          注意: 在這里,我們通過匿名內部類的方式創建了Random對象,這種寫法大家要慢慢熟悉喔。

          2.3 聲明泛型類

          泛型應用于類上面。例如訂單類(Order),這是一個專門負責封裝訂單里面商品的類,當我們購物生成訂單時,訂單里面可以包含任何商品信息。

          請注意,在類上定義的泛型,在類的變量、方法的參數以及方法中同樣也能使用(靜態方法除外)。

        /**
        * 定義一個訂單類:封裝任意類型的商品信息
        * @param <T>
        */
        public class Order<T> {
        private T t ;//在變量中使用: T表示任意商品類型
        public T get(){//在普通方法中使用:T表示任意商品類型
        return t;
        }
        public void set(T t){//在方法的參數使用: T表示任意類型
        this.t = t;
        }
        //測試:
        public static void main(String[] args) {
        Order<Phone> order = new Order<Phone>();//創建訂單對象:封裝Phone商品
        order.set(new Phone("華為Mate20",3899.0));
        System.out.println("商品名稱:"+order.get().getPhoneName());
        }
        }
        //定義手機商品類
        class Phone{
        private String phoneName;
        private Double phonePrice;

        public Phone(String phoneName, Double phonePrice) {
        this.phoneName = phoneName;
        this.phonePrice = phonePrice;
        }

        public Phone() {
        }

        public String getPhoneName() {
        return phoneName;
        }

        public void setPhoneName(String phoneName) {
        this.phoneName = phoneName;
        }

        public Double getPhonePrice() {
        return phonePrice;
        }

        public void setPhonePrice(Double phonePrice) {
        this.phonePrice = phonePrice;
        }
        }

          ok,泛型類我們聲明完成了,大家看一下是不是和我們聲明泛型接口很相似啊,確實是一樣的。

          聲明的語法就是:類名,在這里T可以表示任意類型。

          小伙伴也可以看到,我們定義了一個帶泛型的Order類,在我們創建訂單對象時,可以傳入任意類型的商品對象,使我們的操作更加靈活

          2.4 聲明泛型方法

          泛型應用于方法上面。前面說過在泛型類上定義的泛型,在類的方法中也能使用(靜態方法除外)。但是有的時候我們只想在某個方法上使用泛型,而不是整個類,這也是被允許的,下面我和小伙們一起來體驗一下。

          比如FactoryBean工廠類,通過泛型方法,創建任意類型的對象。

        package cn.qf;
        /**
        * 定義一個工廠Bean:
        */
        public class FactoryBean {
        /*
        定義不帶泛型的方法
        */
        public static Object createObject0(String className) throws Exception{
        return Class.forName(className).newInstance();
        }
        /*
        定義一個普通的泛型方法:className表示類的全路徑
        */
        public <T> T createObject1(String className) throws Exception{
        return (T) Class.forName(className).newInstance();
        }
        /*
        定義一個靜態的泛型方法:className表示類的全路徑
        */
        public static <T> T createObject2(String className)throws Exception{
        return (E) Class.forName(className).newInstance();
        }
        //測試:
        public static void main(String[] args) throws Exception {
        //創建一個Phone對象 : 不使用泛型方法,需要類型強轉
        Phone p1 = (Phone) FactoryBean.createObject0("cn.qf.Phone");
        //創建一個Phone對象 :泛型方法,不需要類型強轉
        Phone p2 = FactoryBean.createObject2("cn.qf.Phone");
        }
        }

        class Phone{
        private String phoneName;
        private Double phonePrice;
        ----
        }

          在這里我們使用工廠模式來創建對象,為了在我們獲取對象時,不用類型強轉,我們也使用了泛型。小伙伴通過代碼可以看到,不使用泛型的方法,在獲取對象時,需要類型強轉(可能會引起類型強轉異常)。

          在使用泛型方法獲取對象時,不需要類型強轉(可以避免引起類型強轉異常)。

          2.5 泛型方法、泛型接口、泛型類小結

          從上面的介紹小伙伴也看到了,泛型類的好處就是在泛型類上定義的泛型,在類的方法中也能使用(普通靜態方法除外)。而泛型方法的最大優點就是能獨立于類,不受類是否是泛型類的限制。因此當你考慮使用泛型的時候,優先考慮定義泛型方法。如果非要定義泛型類,

          個人建議通過使用泛型方法來將整個類泛型化,因為這樣就不用擔心靜態方法的事,如果有靜態方法那必然是泛型方法。這樣就能避免普通靜態方法無法獲取泛型類泛型的尷尬局面。

          你以為這就把泛型介紹完了嗎?并沒有,小伙伴們先休息片刻,稍后我們繼續喔。

          闖關練習

          需求:

          定義一個泛型類:

          包含與類的泛型一樣的變量,

          包含與類的泛型一樣的方法,參數也使用泛型

          同時定義一個類的泛型不相同的泛型方法

          答案:

        /**
        * 定義泛型類:
        * @param <T>: 泛型T
        */
        public class GenericDemo4<T> {
        //1.定義一個與T 一樣的變量
        private T t;
        //2.定義一個與T一樣的方法
        public T test1(T outer){
        System.out.println(outer);
        return outer;
        }
        //3.定義一個與T不一樣的方法
        public <E> E test2(E e){
        System.out.println("自定義泛型的方法:"+e);
        return e;
        }
        //測試:
        public static void main(String[] args) {
        //1.創建對象:指定T的泛型為 String
        GenericDemo4<String> gt = new GenericDemo4<String>();
        //2.調用 與T 一樣的泛型方法
        gt.test1("hello world");
        //3.調用 與T 不一樣的泛型方法
        gt.test2(10);
        }
        }

        好程序員公眾號

        • · 剖析行業發展趨勢
        • · 匯聚企業項目源碼

        好程序員開班動態

        More+
        • HTML5大前端 <高端班>

          開班時間:2021-04-12(深圳)

          開班盛況

          開班時間:2021-05-17(北京)

          開班盛況
        • 大數據+人工智能 <高端班>

          開班時間:2021-03-22(杭州)

          開班盛況

          開班時間:2021-04-26(北京)

          開班盛況
        • JavaEE分布式開發 <高端班>

          開班時間:2021-05-10(北京)

          開班盛況

          開班時間:2021-02-22(北京)

          開班盛況
        • Python人工智能+數據分析 <高端班>

          開班時間:2021-07-12(北京)

          預約報名

          開班時間:2020-09-21(上海)

          開班盛況
        • 云計算開發 <高端班>

          開班時間:2021-07-12(北京)

          預約報名

          開班時間:2019-07-22(北京)

          開班盛況
        在線咨詢
        試聽
        入學教程
        立即報名

        Copyright 2011-2023 北京千鋒互聯科技有限公司 .All Right 京ICP備12003911號-5 京公網安備 11010802035720號

        黑人100部Av解禁片
        <span id="35v3v"><th id="35v3v"></th></span>
        <address id="35v3v"></address>

          <address id="35v3v"></address>
            <address id="35v3v"><listing id="35v3v"><meter id="35v3v"></meter></listing></address>
              <listing id="35v3v"><listing id="35v3v"></listing></listing>

              <listing id="35v3v"><listing id="35v3v"><menuitem id="35v3v"></menuitem></listing></listing>

              <noframes id="35v3v"><noframes id="35v3v">