最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java通俗易懂講解泛型

 更新時間:2022年05月10日 10:16:00   作者:xaiobit_hl  
在正式進入內容之前說明一下:泛型的內容太多,也太復雜。這里因為Java中寫數(shù)據(jù)結構的時候會使用到,所以加上。關于泛型我找了挺多文章,再結合自己的理解,盡可能將其講清楚。不求會使用泛型,只要求后面數(shù)據(jù)結構出現(xiàn)泛型的時候能夠知道是在干什么即可

1.什么是泛型

泛型是在JDK1.5引入的新的語法,通俗講,泛型:就是適用于許多許多類型。從代碼上講,就是對類型實現(xiàn)了參數(shù)化。

看到標紅的這句話,有些兄弟可能會有疑惑,我們以前傳參,傳得是一個整形數(shù)據(jù),傳得是一個引用,我從來沒傳過類型嘛,類型難道還能作為參數(shù)傳遞???對,沒錯,接下來,我們就來看一下類型作為參數(shù)傳遞的實現(xiàn)方式

2.引出泛型

我們以前學過繼承和多態(tài),我們知道所有類的父類默認是Object類,我們現(xiàn)在目標是:數(shù)組中可以存放任何類型的數(shù)據(jù),這時候我們就可以new一個Object類型的數(shù)組,,,

class MyArray {
    public Object[] array = new Object[10];
    public Object getPos(int pos) {
        return array[pos];
    }
    public void setPos(int pos, Object value) {
        array[pos] = value;
    }
}
public class TestDemo {
    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.setPos(0,1);
        myArray.setPos(1,"hello");
    }
}

當我們寫出這樣一個代碼的時候,我們發(fā)現(xiàn)數(shù)組中什么元素都能放了,可以放整形數(shù)據(jù),可以放字符串,還可以放小數(shù),這里面能放的東西太多了,你能知道某個下標的元素是什么樣的類型嗎??當我們取元素的時候,會發(fā)現(xiàn)你的代碼編譯都不能通過,,,

為什么會出現(xiàn)這樣的情況呢,我相信大部分兄弟們都知道,我們觀察getPos的返回值是Object,也就意味著我們拿出來的可能是任意元素,但編譯器知道嗎???它只知道返回的是一個Object:這就很明顯了,父類給子類,,這不純純的向下轉型嗎,向下轉型就必須要強轉類型,否則你的代碼就不可能通過。

那么問題又來了,,我這里只是放了兩個元素,肉眼還能看的過來,但是這里面的類型時刻在發(fā)生改變,就算你知道類型,你每次都需要強轉嗎,我是動態(tài)的啊,我代碼是寫死的啊,所以這個地方如果用Object去做的話,會顯得格外麻煩,而且局限性非常的大

我們現(xiàn)在的困境有兩個:

1.任何類型都可以放,不好控制,,,

2.每次取元素的時候,都得進行強制類型轉換,,,

這時候我們得搞一手希望工程,我們的希望是:

1.我們能不能自己指定類型

2.我們能不能,不再進行類型轉換呀,能不能把這一步省略呀,兄弟?。?!

為了解決這一問題,我們java就引入了這個東西----》泛型

那到底怎么指定類型呢,來看代碼:

class MyArray <T>{
    //public Object[] array = new Object[10];
    public T[] array = (T[]) new Object[10];//這樣寫也不好,待會說為什么??
    public T getPos(int pos) {
        return array[pos];
    }
    public void setPos(int pos, T value) {
        array[pos] = value;
    }
}
public class TestDemo {
    public static void main(String[] args) {
        //我指定放整形
        MyArray<Integer> myArray = new MyArray<>();
        //后面的尖括號里的類型可以省略,在實例化對象的時候根據(jù)前面可以做出判斷
        //MyArray<Integer> myArray = new MyArray<Integer>();
        myArray.setPos(0,1);
        myArray.setPos(1,2);
        Integer ret = myArray.getPos(1);
        System.out.println(ret);
        //我指定放字符串類型
        MyArray<String> myArray2 = new MyArray<>();
        myArray2.setPos(0,"abc");
        myArray2.setPos(1,"bit");
        String ret2 = myArray2.getPos(0);
        System.out.println(ret2);
    }
}

這個代碼有幾個注意事項:

1.尖括號<>里面只能放引用類型;

2.<T>是一個占位符,代表當前類是一個泛型類;

3.MyArray<Integer> array = new MyArray<>();后面的尖括號可以省略Integer不寫,實例化對象的 時候編譯器根據(jù)前面能做出判斷。

看完上面的代碼,我相信兄弟們已經(jīng)明白是怎么一回事了吧

我們剛剛的希望工程也已經(jīng)實現(xiàn)了

1.<Integer>,指定當前類中,使用的類型是Integer類型

2.泛型幫我在編譯期間做了2件事情(目前為止的優(yōu)點):

  • 存放元素的時候,進行類型的檢查
  • 取元素的時候,幫我進行類型的轉換

3.泛型類的語法

有了剛剛知識的鋪墊,這一塊咱就形式化一下啦,,當然,語法還是很重要滴??!

語法:

泛型類<類型實參> 變量名; // 定義一個泛型類引用 
new 泛型類<類型實參>(構造方法實參); // 實例化一個泛型類對象

栗子:

MyArray<Integer> list = new MyArray<Integer>();

4.裸類型

說明:

兄弟們,咱學了新東西,這舊東西,咱也得了解一下是不咯,,萬一以后跟別人聊到了,自己好像從來沒聽過就尷尬了,,

裸類型是一個泛型類但沒有帶著類型實參,例如 MyArrayList 就是一個裸類型

MyArray list = new MyArray();

注意: 我們不要自己去使用裸類型,裸類型是為了兼容老版本的 API 保留的機制

5.泛型如何編譯的

5.1 擦除機制

我用的是Powershell窗口查看的字節(jié)碼文件,當然用idea的兄弟們也可以去下載一個展示字節(jié)碼的插件,,接下來我們看看泛型到底是怎么編譯的,,javap -c查看字節(jié)碼

我們發(fā)現(xiàn)在編譯期間,所有的T都被擦除為了Object,那既然所有的T都變成了Object,那我們?yōu)槭惨付愋湍??注意這里不是編譯的時候替換,指定類型只是為了在編譯的時候幫我們進行類型的檢查和轉換,并不是替換?。?!最終字節(jié)碼編譯完成的時候,所有的T變成了Object,而這就是我們Java當中泛型所謂的擦除機制?。?!

提問:那既然T在編譯的時候被擦除為了Object,那為啥 T[] array = new T[] 會報錯呢?

5.2會給你答案??????

5.2.泛型數(shù)組為什么不能實例化

先看一段代碼和運行結果:

class MyArray<T> {
    public T[] array = (T[])new Object[10];
    public T getPos(int pos) { return this.array[pos]; }
    public void setVal(int pos,T val) { this.array[pos] = val; }
    public T[] getArray() { return array; }
}
public class TestDemo {
    public static void main(String[] args) {
        MyArray<Integer> myArray1 = new MyArray<>();
        Integer[] strings = myArray1.getArray();//這里運行時會報錯
        String[] strings1 = (String)myArray1.getArray();//這里編譯都不能通過
    }
}

出現(xiàn)這種情況的原因何在呢?

這里最大的問題就是數(shù)組,java中的數(shù)組是非常特殊的,它是在堆上分配內存的,它有自己的一套特殊的機制,而T[] array = new T[] 這個數(shù)組它沒有指定具體的類型,肯定是不讓這樣做的;再者,get方法的返回類型是Object類型,如果用整形數(shù)組來接受,父類給子類,編譯器是直接不讓你做的。

那問題來了???既然 T[] array = new T[] 這種寫法是錯誤的,T[] array = (T[])new Object[10] 這種寫法又不好,那正確的寫法應該是怎么的呢???

正確寫法:(了解即可)

/**
 * 通過反射創(chuàng)建指定類型的數(shù)組
 */
public MyArray(Class<T> clazz, int capacity) {
    array = (T[]) Array.newInstance(clazz, capacity);
}

看起來很復雜,對吧,但是沒關系,java官方都不這么用,所以我們也只需要了解即可。

我們可以看下ArrayList的源碼來確認一下(以免存在忽悠的成份):

6.泛型的上界

什么叫泛型的上界呢??先給你們看一下語法:

class 泛型類名稱<類型形參 extends 類型邊界> {
       ...
}

這一模塊我將舉兩個栗子來和大家一起認識或溫習一下這個知識點,,,

簡單示例:

class Test<T extends Number> {
}
public class TestDemo {
    public static void main(String[] args) {
        //Number的子類
        Test<Integer> test = new Test<>();
        Test<Double> test1 = new Test<>();
        Test<Float> test2 = new Test<>();
        //Number本身
        Test<Number> test3 = new Test<>();
    }
}

<T extends Number>中的 extends可不敢理解為繼承喔,可以理解為T擦除為了Number的子類或自己本身,也就是說在主函數(shù)中用這個類實例化的時候,可以指定的類型一定要 <= Number ,,差不多就這意思,,,簡單的你們都能理解,我就不多廢話了,給你們來點稍微有難度的(我覺得難)

進階示例:

class Alg<T extends Comparable<T>> {
    //如果不設置上界為Comparable,就用不了compareTo這個方法,因為T如果不指定的話,
    // 在編譯的時候被擦除成了Object,而Object并沒有實現(xiàn)這個接口
    public T findMax(T[] array) {
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(max.compareTo(array[i]) < 0) {
                max =  array[i];
            }
        }
        return max;
    }
}
class Student implements Comparable<Student> {
    public String name;
    public int age;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
    @Override
    public int compareTo(Student o) {
        return 0;
    }
}
public class TestDemo {
    public static void main(String[] args) {
        //指定類型為整形
        Alg<Integer> alg = new Alg<>();
        Integer[] array = {1,2,3,4,5};
        Integer max = alg.findMax(array);
        System.out.println(max);
        //指定類型為Student
        Alg<Student> alg2 = new Alg<>();
        Student[] students = new Student[3];
        students[0] = new Student("毛毛",15);
        students[1] = new Student("吉吉",13);
        students[2] = new Student("毛二",16);
        Student student = alg2.findMax(students);
        System.out.println(student);
    }
}

<T extends Comparable<T>>意為擦除到實現(xiàn)了這個接口,簡而言之就是這里面接收的類型一定是實現(xiàn)了Comparable接口的,給你們證明一下,我那里面Student類型毋庸置疑,自己實現(xiàn)的Comparable接口,我們來看看Integer的源碼:

再者,我們觀察一下 Alg 這個類中的 findMax 方法,我們都知道引用類型比較大小要用比較器或者compareTo方法,如果 Student 這個類沒有實現(xiàn) Comparable 接口,我們在寫代碼的時候,會發(fā)現(xiàn)點都點不出來,比較一下,就通透了,,

沒有實現(xiàn)Comparable接口:

實現(xiàn)了Comparable接口:

當然,又有人會問,我能不能不實例化這個方法,直接調用這個方法,這時候我們把這個方法搞成靜態(tài)的來看一下:

發(fā)現(xiàn)它報錯了,,,別急哈,咱們一步一步是爪牙,,哦,不對,是一步一步來揭開面紗,,

那如何將這個代碼改成正確的呢??細心的兄弟會發(fā)現(xiàn),既然這個方法是靜態(tài)的, 那我們只需要通過類名來方法,如果還按照之前的寫法,類后面跟一個 <T>,那我們啥時候給它指定類型??不知道吧,這時候我們要將代碼改成這樣,在 static 的后面跟一個 <T>,這樣你的代碼就不會報錯了,,,

當然,我們也可以指定邊界,這里跟類有著異曲同工之妙,就不細說了,花兩秒鐘看一下代碼,你就能發(fā)出: 哦~~~~~的聲音,中國人不騙中國人

class Alg2 <T>{
    public static<T extends Comparable> T findMax(T[] array) {
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(max.compareTo(array[i]) < 0) {
                max =  array[i];
            }
        }
        return max;
    }
}

最后,你們可能會問,那它既然有上界,那它有下界嗎??

那肯定是沒有的啊,咱學習編程多辛苦啊 ??!是不是兄弟們

7.通配符

兄弟們,最后兩個知識點,咱泛型就告一段落了,,

什么是通配符??我還是換一行吧

?這個就是通配符,對,沒錯,就是這個問號 "?"

7.1.通配符能用來干嘛

JAVA中引入通配符這一玩意是用來解決泛型無法協(xié)變這一問題的,那什么是協(xié)變呢?

協(xié)變這東西怎么說呢,類似于,繼承當中的父類引用子類的對象,但是泛型中不支持協(xié)變類型,而JAVA的數(shù)組是支持的,數(shù)組中明確了它所允許存儲的對象類型,并且會在運行時做類型檢查(這也是為什么不能創(chuàng)建泛型數(shù)組的原因,數(shù)組創(chuàng)建時必須知道確切類型)。

下面看具體代碼,看看是如何解決協(xié)變問題的,,,

class Message<T> {
    private T message;
    public T getMessage() {
        return message;
    }
    public void setMessage(T message) {
        this.message = message;
    }
}
public class TestDemo2 {
    public static void main(String[] args) {
        Message<Integer> message = new Message<>();
        message.setMessage(1);
        fun(message);//編譯不通過
    }
    public static void fun(Message<String> temp) {
        System.out.println(temp.getMessage());
    }
}

首先,上面這一段代碼編譯是不能通過的,很明顯我 fun 方法中只能接收 String 類型的數(shù)據(jù),而你給我一個整形數(shù)據(jù),肯定會報錯嗎,當我把它改成這樣的時候,,你會發(fā)現(xiàn)代碼不報錯了:

class Message<T> {
    private T message;
    public T getMessage() {
        return message;
    }
    public void setMessage(T message) {
        this.message = message;
    }
}
public class TestDemo2 {
    public static void main(String[] args) {
        Message<Integer> message = new Message<>();
        message.setMessage(1);
        fun(message);
    }
    public static void fun(Message<?> temp) {
        //temp.setMessage(1);//是不能往里面加元素的,所以源碼當中通配符用的比較多
        System.out.println(temp.getMessage());
    }
}

這里尖括號里面放?意味著可以接收任何類型,人家用戶你可不知道你這是什么類型,所以通配符在這一塊就起了非常大的作用;再者,既然可以接收所有類型,就不能讓用戶隨便修改。具體是怎么做到的呢?也就是說當你用通配符在函數(shù)中接收任意類型之后,你不能調用 set 方法往里面設值,你根本不知道你接收到的是什么類型,,,所以,我們也看到源碼當中通配符用的比較多就是這么回事

7.2.通配符的上界(讀數(shù)據(jù))

通配符的上界,這東西跟剛剛的泛型上界很相似,有了剛剛的只是鋪墊,應該不難理解了,,語法這一塊咱就不演示了

class Food {}
class Fruit extends Food {}
class Apple extends Fruit {}
class Banana extends Fruit {}
class Plate<T> {
    private T pear;
    public T getPlate() {
        return pear;
    }
    public void setPlate(T a) {
        this.pear = a;
    }
}
public class TestDemo3 {
    public static void main(String[] args) {
        Plate<Apple> plate = new Plate<>();
        Plate<Banana> plate1 = new Plate<> ();
        fun1(plate);
        fun1(plate1);
    }
    public static void fun1(Plate<? extends Fruit> temp) {
        //temp.setPlate(new Apple());這里不知道temp引用的子類是誰,所以不能往里邊放元素
        //可以接收元素,父類接收子類,頂多就是向上轉型
        System.out.println(temp.getPlate());
        //Fruit fruit = temp.getPlate();
    }
}

我們這里給了它香蕉/蘋果等一些水果類和 Fruit 這個父類,<? extends Fruit>這里可以接收任何Fruit 的子類類型,同理,既然可以接收任何類型,就不適合寫數(shù)據(jù),只適合讀數(shù)據(jù),,

7.3.通配符的下界(寫數(shù)據(jù))

通配符不同于泛型,它是有下界的,來看看它的下界,,,

語法大概就這樣子,簡單了解一下:<? super 下界>---重點在代碼

public class TestDemo3 {
    public static void main(String[] args) {
        Plate<Fruit> plate = new Plate<>();
        fun(plate);
        Plate<Food> plate2 = new Plate<>();
        fun(plate2);
        Plate<Apple> plate1 = new Plate<>();
        //fun(plate1);
    }
    public static void fun(Plate<? super Fruit> temp) {
        //寫數(shù)據(jù),但是不能寫父類
        temp.setPlate(new Apple());
        temp.setPlate(new Banana());
        //編譯不通過:Fruit的父類有很多,不一定就是某一個
        //temp.setPlate(new Food());
        //不安全--它的父類有很多,不知道是哪一個父類發(fā)生了向下轉型
        //Fruit fruit = (Fruit)temp.getPlate();
    }
}

通配符的下界呢,,我們可以看到它是用了一個關鍵字super,字面意思,大家都懂,就是說能接收的類型至少是 Fruit ,以及 Fruit 的父類,所以,在讀寫數(shù)據(jù)這一塊,它就不同于別人,它只喜歡寫數(shù)據(jù),就像咱人一樣,總得有些偏執(zhí)的愛好,我就喜歡敲代碼,是吧,,,為啥不能讀數(shù)據(jù),這里還是得解釋一下,因為 Fruit 的父類太多了,你根本不知道讀誰的數(shù)據(jù),再者就算你強轉類型,編譯器也會認為這是不安全的,所以我們在讀數(shù)據(jù)和寫數(shù)據(jù)這一塊,?的上界和?的下界要搞清楚什么時候用哪一個,,,

下圖能幫助你理解讀寫數(shù)據(jù),到底適合哪一個

本來還有一個裝箱和拆箱的類容要給兄弟們分享的,但是現(xiàn)在已經(jīng)凌晨兩點了(小命要緊,建議大家不要像我這樣,身體是內卷的本錢,我只是一時興起,不敢瞞著兄弟們偷偷卷),小編我要為那些目前還在渴望這一塊知識的年輕小伙子著想,就把那些內容單獨抽到下一期去了,,,下期見,兄弟們?。?!

到此這篇關于Java通俗易懂講解泛型的文章就介紹到這了,更多相關Java泛型內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • idea構建web項目的超級詳細教程

    idea構建web項目的超級詳細教程

    好多朋友在使用IDEA創(chuàng)建項目時,總會碰到一些小問題,下面這篇文章主要給大家介紹了關于idea構建web項目的超級詳細教程,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2023-03-03
  • idea debug沒有force step into的問題解決

    idea debug沒有force step into的問題解決

    本文主要介紹了IDEA Debug中ForceStepInto按鈕消失的問題及解決方法,文中通過圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-10-10
  • 使用Java對Hbase操作總結及示例代碼

    使用Java對Hbase操作總結及示例代碼

    這篇文章主要介紹了使用Java對Hbase進行操作總結,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • SpringBoot應用部署之WAR包部署方式

    SpringBoot應用部署之WAR包部署方式

    這篇文章主要介紹了SpringBoot應用部署之WAR包部署方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • 逆波蘭計算器(Java實現(xiàn))

    逆波蘭計算器(Java實現(xiàn))

    這篇文章主要為大家詳細介紹了Java實現(xiàn)逆波蘭計算器,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • Java遍歷Properties所有元素的方法實例

    Java遍歷Properties所有元素的方法實例

    這篇文章主要介紹了Java如何遍歷Properties所有元素的方法,大家可以參考使用
    2013-11-11
  • 深入理解java動態(tài)代理機制

    深入理解java動態(tài)代理機制

    本篇文章主要介紹了深入理解java動態(tài)代理機制,詳細的介紹動態(tài)代理有哪些應用場景,什么是動態(tài)代理,怎樣使用,它的局限性在什么地方?有興趣的可以了解一下。
    2017-02-02
  • java實現(xiàn)點贊功能

    java實現(xiàn)點贊功能

    這篇文章主要為大家詳細介紹了java實現(xiàn)點贊功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • Java如何基于IO流實現(xiàn)同一文件讀寫操作

    Java如何基于IO流實現(xiàn)同一文件讀寫操作

    這篇文章主要介紹了Java如何基于IO流實現(xiàn)文件讀寫操作,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-10-10
  • mybatis的association傳遞參數(shù)問題示例

    mybatis的association傳遞參數(shù)問題示例

    這篇文章主要介紹了mybatis的association傳遞參數(shù)問題,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-12-12

最新評論

获嘉县| 南丹县| 连平县| 茶陵县| 巴林左旗| 讷河市| 石家庄市| 石河子市| 阜阳市| 伊宁市| 宜春市| 舞钢市| 白朗县| 云安县| 青河县| 体育| 乌兰浩特市| 隆德县| 嫩江县| 普兰店市| 章丘市| 怀宁县| 利辛县| 苏州市| 兴安盟| 桑植县| 临城县| 高雄市| 华阴市| 昌平区| 巴里| 泾川县| 当涂县| 汤原县| 同德县| 江口县| 潍坊市| 逊克县| 禄丰县| 光泽县| 贵德县|