偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

了解泛型擦除嗎?知道類型擦除會造成多態(tài)的沖突嗎?如何解決?

開發(fā) 前端
如果是重載,那么子類中兩個setValue方法,一個是參數(shù)Object類型,一個是Date類型,可是根本就沒有這樣的一個子類繼承自父類的Object類型參數(shù)的方法。所以說,確實是重寫了,而不是重載了。

泛型的代碼只存在于編譯階段,在進入JVM之前,與泛型相關(guān)的信息會被擦除掉,稱之為類型擦除。

  • 無限制類型擦除:當在類的定義時沒有進行任何限制,那么在類型擦除后將會被替換成Object,例如<T>、<?> 都會被替換成Object。
  • 有限制類型擦除:當類定義中的參數(shù)類型存在上下限(上下界),那么在類型擦除后就會被替換成類型參數(shù)所定義的上界或者下界,例如<? extend Person>會被替換成Person,而<? super Person> 則會被替換成Object。

泛型的橋接方法

類型擦除會造成多態(tài)的沖突,而JVM解決方法就是橋接方法。

舉例

現(xiàn)在有這樣一個泛型類:

class Pair<T> {  

    private T value;  

    public T getValue() {  
        return value;  
    }  

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

然后一個子類繼承它

class DateInter extends Pair<Date> {  

    @Override  
    public void setValue(Date value) {  
        super.setValue(value);  
    }  

    @Override  
    public Date getValue() {  
        return super.getValue();  
    }  
}

在這個子類中,設(shè)定父類的泛型類型為Pair<Date>,在子類中,覆蓋了父類的兩個方法,原意是這樣的:將父類的泛型類型限定為Date,那么父類里面的兩個方法的參數(shù)都為Date類型。

public Date getValue() {  
    return value;  
}  

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

實際上,類型擦除后,父類的的泛型類型全部變?yōu)榱嗽碱愋蚈bject,所以父類編譯之后會變成下面的樣子:

class Pair {  
    private Object value;  

    public Object getValue() {  
        return value;  
    }  

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

再看子類的兩個重寫的方法的類型:setValue方法,父類的類型是Object,而子類的類型是Date,參數(shù)類型不一樣,這如果實在普通的繼承關(guān)系中,根本就不會是重寫,而是重載。 在一個main方法測試一下:

public static void main(String[] args) throws ClassNotFoundException {  
        DateInter dateInter = new DateInter();  
        dateInter.setValue(new Date());                  
        dateInter.setValue(new Object()); //編譯錯誤  
}

如果是重載,那么子類中兩個setValue方法,一個是參數(shù)Object類型,一個是Date類型,可是根本就沒有這樣的一個子類繼承自父類的Object類型參數(shù)的方法。所以說,確實是重寫了,而不是重載了。

為什么這樣?

原因是這樣的,傳入父類的泛型類型是Date,Pair<Date>,本意是將泛型類變?yōu)槿缦拢?/p>

class Pair {  
    private Date value;  
    public Date getValue() {  
        return value;  
    }  
    public void setValue(Date value) {  
        this.value = value;  
    }  
}

然后在子類中重寫參數(shù)類型為Date的兩個方法,實現(xiàn)繼承中的多態(tài)。

可是由于種種原因,虛擬機并不能將泛型類型變?yōu)镈ate,只能將類型擦除掉,變?yōu)樵碱愋蚈bject。這樣,原來是想進行重寫,實現(xiàn)多態(tài),可是類型擦除后,只能變?yōu)榱酥剌d。這樣,類型擦除就和多態(tài)有了沖突。于是JVM采用了一個特殊的方法,來完成這項功能,那就是橋方法。

原理

用javap -c className的方式反編譯下DateInter子類的字節(jié)碼,結(jié)果如下:

class com.tao.test.DateInter extends com.tao.test.Pair<java.util.Date> {  
  com.tao.test.DateInter();  
    Code:  
       0: aload_0  
       1: invokespecial #8                  // Method com/tao/test/Pair."<init>":()V  
       4: return

public void setValue(java.util.Date);  //我們重寫的setValue方法  
    Code:  
       0: aload_0  
       1: aload_1  
       2: invokespecial #16                 // Method com/tao/test/Pair.setValue:(Ljava/lang/Object;)V  
       5: return

public java.util.Date getValue();    //我們重寫的getValue方法  
    Code:  
       0: aload_0  
       1: invokespecial #23                 // Method com/tao/test/Pair.getValue:()Ljava/lang/Object;  
       4: checkcast     #26                 // class java/util/Date  
       7: areturn  

public java.lang.Object getValue();     //編譯時由編譯器生成的橋方法  
    Code:  
       0: aload_0  
       1: invokevirtual #28                 // Method getValue:()Ljava/util/Date 去調(diào)用我們重寫的getValue方法;  
       4: areturn  

  public void setValue(java.lang.Object);   //編譯時由編譯器生成的橋方法  
    Code:  
       0: aload_0  
       1: aload_1  
       2: checkcast     #26                 // class java/util/Date  
       5: invokevirtual #30                 // Method setValue:(Ljava/util/Date; 去調(diào)用我們重寫的setValue方法)V  
       8: return
}

從編譯的結(jié)果來看,本意重寫setValue和getValue方法的子類,但是反編譯后竟然有4個方法,其實最后的兩個方法,就是編譯器自己生成的橋方法??梢钥吹綐蚍椒ǖ膮?shù)類型都是Object,也就是說,子類中真正覆蓋父類兩個方法的就是這兩個我們看不到的橋方法。而在setvalue和getValue方法上面的@Oveerride只不過是假象。而橋方法的內(nèi)部實現(xiàn),就只是去調(diào)用自己重寫的那兩個方法。

所以,虛擬機巧妙的使用了橋方法,來解決了類型擦除和多態(tài)的沖突。

并且,還有一點也許會有疑問,子類中的橋方法Object getValue()和Date getValue()是同時存在的,可是如果是常規(guī)的兩個方法,他們的方法簽名是一樣的,如果是我們自己編寫Java代碼,這樣的代碼是無法通過編譯器的檢查的(返回值不同不能作為重載的條件),但是虛擬機卻是允許這樣做的,因為虛擬機通過參數(shù)類型和返回類型來確定一個方法,所以編譯器為了實現(xiàn)泛型的多態(tài)允許自己做這個看起來“不合法”的事情,然后交給虛擬機去區(qū)別

責(zé)任編輯:武曉燕 來源: sevenCoding
相關(guān)推薦

2022-03-02 14:41:03

泛型反序列化

2024-06-07 10:05:31

2021-08-24 08:05:41

泛型類型擦除Class

2024-01-15 08:28:31

Spring事件

2019-09-04 00:20:10

JSON泛型擦除

2023-03-06 08:33:24

IDEA反編譯類型

2020-12-21 16:18:07

JavaTypeToken泛型擦除

2025-01-13 07:00:00

Java泛型編程

2021-07-29 09:20:18

Java泛型String

2021-07-01 06:47:30

Java泛型泛型擦除

2022-03-31 09:01:10

Swift類型擦除類型安全性

2021-06-29 09:01:50

Swift閉包語言

2024-09-11 16:56:39

2023-05-30 18:13:59

Git代碼

2018-06-27 09:51:17

2021-06-24 09:08:34

Java代碼泛型

2023-10-11 12:35:29

Maven

2023-11-29 08:19:45

Go泛型缺陷

2022-05-09 14:09:23

多線程線程安全

2016-10-31 20:56:57

Javascript閉包內(nèi)存泄漏
點贊
收藏

51CTO技術(shù)棧公眾號