亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Java基礎(chǔ)-泛型詳解

hearaway / 2057人閱讀

摘要:泛型方法可以定義在普通類和泛型類中,泛型方法可以被修飾符修飾。泛型類型變量會(huì)在編譯后被擦除,用第一個(gè)限定類型替換沒有限定類型的用替換。比如通配符類型代表任何泛型類型的類型變量是和的子類。這個(gè)類型參數(shù)有一個(gè)子類型限定,其自身又是一個(gè)泛型類型。

我的博客   轉(zhuǎn)載請注明原創(chuàng)出處。

之所以會(huì)想來寫泛型相關(guān)的內(nèi)容,是因?yàn)榭吹竭@樣的一段代碼:

當(dāng)時(shí)我的內(nèi)心是這樣的:

所以就趕緊去復(fù)習(xí)了下,記錄下來?;A(chǔ)不扎實(shí),源碼看不懂啊。

泛型介紹

Java 泛型(generics)是 JDK 5 中引入的一個(gè)新特性,泛型提供了編譯時(shí)類型安全檢測機(jī)制,該機(jī)制允許程序員在編譯時(shí)檢測到非法的類型。泛型的本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù),在Java集合框架里使用的非常廣泛。

定義的重點(diǎn)是提供了編譯時(shí)類型安全檢測機(jī)制。比如有這樣的一個(gè)泛型類:

public class Generics  {

    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

然后寫這樣一個(gè)類:

public class Generics  {

    private Object value;

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }
}

它們同樣都能存儲(chǔ)所有值,但是泛型類有編譯時(shí)類型安全檢測機(jī)制:

泛型類

一個(gè)類定義了一個(gè)或多個(gè)類型變量,那么就是泛型類。語法是在類名后用尖括號(hào)括起來,類型變量寫在里面用逗號(hào)分開。然后就可以在方法的返回類型、參數(shù)和域、局部變量中使用類型變量了,但是不能在有static修飾符修飾的方法或域中使用。例子:

類定義參考上文例子
使用形式
Generics generics = new Generics();
后面尖括號(hào)內(nèi)的內(nèi)容在Jdk7以后可以省略
Generics generics = new Generics<>();
泛型方法

一個(gè)方法定義了一個(gè)或多個(gè)類型變量,那么就是泛型方法。語法是在方法修飾符后面、返回類型前面用尖括號(hào)括起來,類型變量寫在里面用逗號(hào)分開。泛型方法可以定義在普通類和泛型類中,泛型方法可以被static修飾符修飾。
例子:

    private  void out(U u) {
        System.out.println(u);
    }
    
    調(diào)用形式,
    Test.out("test");
    大部分情況下都可以省略,編譯器可以推斷出來類型
    Test.out("test");
    
類型變量的限定

有時(shí)候我們會(huì)有希望限定類型變量的情況,比如限定指定的類型變量需要實(shí)現(xiàn)List接口,這樣我們就可以在代碼對類型變量調(diào)用List接口里的方法,而不用擔(dān)心會(huì)沒有這個(gè)方法。

public class Generics  {

    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

    public void add(Object u) {
        value.add(u);
    }

    public static void main(String[] args) {
        Generics generics = new Generics<>();
        generics.setValue(new ArrayList<>());
        generics.add("ss");
        System.out.println(generics.getValue());
    }
    
}

限定的語法是在類型變量的后面加extends關(guān)鍵字,然后加限定的類型,多個(gè)限定的類型要用&分隔。類型變量和限定的類型可以是類也可以是接口,因?yàn)镴ava中類只能繼承一個(gè)類,所以限定的類型是類的話一定要在限定列表的第一個(gè)。

類型擦除

類型擦除是為了兼容而搞出來的,大意就是在虛擬機(jī)里是沒有泛型類型,泛型只存在于編譯期間。泛型類型變量會(huì)在編譯后被擦除,用第一個(gè)限定類型替換(沒有限定類型的用Object替換)。上文中的Generics 泛型類被擦除后會(huì)產(chǎn)生對應(yīng)的一個(gè)原始類型:

public class Generics {

    private Object value;

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

}

之所以我們能設(shè)置和返回正確的類型是因?yàn)榫幾g器自動(dòng)插入了類型轉(zhuǎn)換的指令。

    public static void main(String[] args) {
        Generics generics = new Generics<>();
        generics.setValue("ss");
        System.out.println(generics.getValue());
    }
    
    javac Generics.java
    javap -c Generics
    編譯后的代碼
     public static void main(java.lang.String[]);
        Code:
       0: new           #3                  // class generics/Generics
       3: dup
       4: invokespecial #4                  // Method "":()V
       7: astore_1
       8: aload_1
       9: ldc           #5                  // String ss
      11: invokevirtual #6                  // Method setValue:(Ljava/lang/Object;)V
      14: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      17: aload_1
      18: invokevirtual #8                  // Method getValue:()Ljava/lang/Object;
      21: checkcast     #9                  // class java/lang/String
      24: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      27: return

我們可以看到在21行插入了一條類型轉(zhuǎn)換的指令。

類型擦除還帶來了另一個(gè)問題,如果我們有一個(gè)類繼承了泛型類并重寫了父類的方法:

public class SubGenerics extends Generics {

    @Override
    public void setValue(String value) {
        System.out.println(value);
    }

    public static void main(String[] args) {
        Generics generics = new SubGenerics();
        generics.setValue("ss");
    }

}

因?yàn)轭愋筒脸?b>SubGenerics實(shí)際上有兩個(gè)setValue方法,SubGenerics自己的setValue(String value)方法和從Generics繼承來的setValue(Object value)方法。例子中的generics引用的是SubGenerics對象,所以我們希望調(diào)用的是SubGenerics.setValue。為了保證正確的多態(tài)性,編譯器在SubGenerics類中生成了一個(gè)橋方法:

public void setValue(Object value) {
    setValue((String) value);
}

我們可以編譯驗(yàn)證下:

Compiled from "SubGenerics.java"
public class generics.SubGenerics extends generics.Generics {
  public generics.SubGenerics();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method generics/Generics."":()V
       4: return

  public void setValue(java.lang.String);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_1
       4: invokevirtual #3                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       7: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #4                  // class generics/SubGenerics
       3: dup
       4: invokespecial #5                  // Method "":()V
       7: astore_1
       8: aload_1
       9: ldc           #6                  // String ss
      11: invokevirtual #7                  // Method generics/Generics.setValue:(Ljava/lang/Object;)V
      14: return

  public void setValue(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #8                  // class java/lang/String
       5: invokevirtual #9                  // Method setValue:(Ljava/lang/String;)V
       8: return
}
引用《Java核心技術(shù) 卷一》

總之,需要記住有關(guān) Java 泛型轉(zhuǎn)換的事實(shí):
1.虛擬機(jī)中沒有泛型,只有普通的類和方法。
2.所有的類型參數(shù)都用它們的限定類型替換。
3.橋方法被合成來保持多態(tài)。
4.為保持類型安全性,必要時(shí)插人強(qiáng)制類型轉(zhuǎn)換。

約束與局限性

類型變量不能是基本變量,比如int,double等。應(yīng)該使用它們的包裝類Integer,Double。

虛擬機(jī)中沒有泛型,所以不能使用運(yùn)行時(shí)的類型查詢,比如 if (generics instanceof Generics) // Error

因?yàn)轭愋筒脸脑?,不能?chuàng)建泛型類數(shù)組,比如Generics[] generics = new Generics[10]; // Error

不能實(shí)例化類型變量,比如new T(...) new T[...] 或 T.class

通配符類型

通配符類型和上文中的類型變量的限定有些類似,區(qū)別是通配符類型是運(yùn)用在聲明的時(shí)候而類型變量的限定是在定義的時(shí)候。比如通配符類型Generics代表任何泛型Generics類型的類型變量是ListList的子類。

Generics generics = new Generics();

不過這樣聲明之后Generics的方法也發(fā)生了變化,變成了

這樣就導(dǎo)致了不能調(diào)用setValue方法

getValue方法是正常的

超類型限定

通配符限定還可以限定超類,比如通配符類型Generics代表任何泛型Generics類型的類型變量是ArrayListArrayList的超類。

Generics generics = new Generics();

同樣的,Generics的方法也發(fā)生了變化,變成了

調(diào)用getValue方法只能賦值給Object變量

調(diào)用setValue方法只能傳入ArrayListArrayList的子類,超類List,Object等都不行

反射和泛型

雖然因?yàn)轭愋筒脸谔摂M機(jī)里是沒有泛型的。不過被擦除的類還是保留了一些關(guān)于泛型的信息,可以使用反射相關(guān)的Api來獲取。

類似地,看一下泛型方法

public static > T min(T[] a)

這是擦除后

public static Comparable min(Coniparable[] a)

可以使用反射 API 來確定:

這個(gè)泛型方法有一個(gè)叫做T的類型參數(shù)。

這個(gè)類型參數(shù)有一個(gè)子類型限定, 其自身又是一個(gè)泛型類型。

這個(gè)限定類型有一個(gè)通配符參數(shù)。

這個(gè)通配符參數(shù)有一個(gè)超類型限定。

這個(gè)泛型方法有一個(gè)泛型數(shù)組參數(shù)。

后記

周一就建好的草稿,到了星期天才寫好,還是刪掉了一些小節(jié)情況下,怕是拖延癥晚期了......不過也是因?yàn)榉盒偷膬?nèi)容夠多,雖然日常業(yè)務(wù)里很少自己去寫泛型相關(guān)的代碼,但是在閱讀類庫源碼時(shí)要是不懂泛型就寸步難行了,特別是集合相關(guān)的。這次的大部分內(nèi)容都是《Java核心技術(shù) 卷一》里的,這可是本關(guān)于Java基礎(chǔ)的好書。不過還是老規(guī)矩,光讀可不行,還是要用自己的語言記錄下來。眾所周知,人類的本質(zhì)是復(fù)讀機(jī),把好書里的內(nèi)容重復(fù)一遍,就等于我也有責(zé)任了!

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/71738.html

相關(guān)文章

  • Java學(xué)習(xí)路線總結(jié),搬磚工逆襲Java架構(gòu)師(全網(wǎng)最強(qiáng))

    摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號(hào)作者架構(gòu)師奮斗者掃描主頁左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點(diǎn)贊收藏留言前情提要無意間聽到領(lǐng)導(dǎo)們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊(duì)的人太少,簡而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...

    Scorpion 評(píng)論0 收藏0
  • 后臺(tái)開發(fā)常問面試題集錦(問題搬運(yùn)工,附鏈接)

    摘要:基礎(chǔ)問題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識(shí)點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對象鎖和類鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...

    spacewander 評(píng)論0 收藏0
  • 后臺(tái)開發(fā)常問面試題集錦(問題搬運(yùn)工,附鏈接)

    摘要:基礎(chǔ)問題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識(shí)點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對象鎖和類鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...

    xfee 評(píng)論0 收藏0
  • 后臺(tái)開發(fā)常問面試題集錦(問題搬運(yùn)工,附鏈接)

    摘要:基礎(chǔ)問題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識(shí)點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對象鎖和類鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...

    makeFoxPlay 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<