泛型:類型擦除

Java 語言引入泛型是為了在編譯時提供更嚴(yán)格的類型檢查,并支持泛型編程。

為了實現(xiàn)泛型,Java編譯器將類型擦除應(yīng)用于:

  • 用邊界值替換泛型類型中的所有類型參數(shù),如果是無限邊界的,則使用 Object 替換。因此,生成的字節(jié)碼只包含普通類、接口和方法。
  • 如果需要,強(qiáng)制類型轉(zhuǎn)換,以確保類型安全。
  • 生成橋接方法來保存擴(kuò)展泛型類型中的多態(tài)性。

類型擦除確保不會為參數(shù)化類型創(chuàng)建新類。因此,泛型不會產(chǎn)生運行時開銷。

泛型類擦除

在類型擦除過程中,Java 編譯器擦除所有類型參數(shù),如果類型參數(shù)有界,則用它的第一個邊界替換每個參數(shù),如果沒有邊界則用 Object 替換。

比如下邊這個單向鏈表節(jié)點的泛型類,

public class Node<T> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

因為類型參數(shù) T 是無邊界的,編譯器會用 Object 替換 T。

public class Node {

    private Object data;
    private Node next;

    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Object getData() { return data; }
    // ...

如果我們把 Node 改成有邊界的泛型類,如下:

public class Node<T extends Comparable<T>> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

編譯器會把 T 替換成第一個邊界值 Comparable,如:

public class Node {

    private Comparable data;
    private Node next;

    public Node(Comparable data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Comparable getData() { return data; }
    // ...
}

泛型方法擦除

Java 編譯器也會擦除泛型方法參數(shù)中的類型參數(shù)。

public static <T> int count(T[] anArray, T elem) {
    int cnt = 0;
    for (T e : anArray)
        if (e.equals(elem))
            ++cnt;
        return cnt;
}

因為 T 是無邊界的,所以編譯器會用 Object 代替它。

public static int count(Object[] anArray, Object elem) {
    int cnt = 0;
    for (Object e : anArray)
        if (e.equals(elem))
            ++cnt;
        return cnt;
}

在比如:

class Shape { /* ... */ }
class Circle extends Shape { /* ... */ }
class Rectangle extends Shape { /* ... */ }

public static <T extends Shape> void draw(T shape) { /* ... */ }

編譯后會變成:

public static void draw(Shape shape) { /* ... */ }

擦除和橋接函數(shù)

有時候因為類型擦除會導(dǎo)致一些意想不到的情況。

下面的例會解釋如何發(fā)生的。這個例子(在橋接方法中描述)向我們展示了編譯器在類型擦除過程中,如何創(chuàng)建一個合成方法(稱為橋接方法)。

public class Node<T> {

    public T data;

    public Node(T data) { this.data = data; }

    public void setData(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node<Integer> {
    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

調(diào)用代碼

public class Node {

    public Object data;

    public Node(Object data) { this.data = data; }

    public void setData(Object data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node {

    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

MyNode mn = new MyNode(5);
Node n = mn;            
n.setData("Hello");     // 編譯沒有報錯,運行時拋出異常
Integer x = mn.data;  

編譯后類型擦除后的代碼:

MyNode mn = new MyNode(5);
Node n = (MyNode)mn;         
n.setData("Hello");
Integer x = (String)mn.data;   

這個異常是怎么造成的呢?
NodesetData 的參數(shù)類型為 Object 的,而 MyNode 需要的是 Integer,這里會報一個 ClassCastException 的異常。

具體的原因,是因為編譯器在編譯一個繼承自泛型類的子類時,為了方法覆蓋的簽名匹配,保留泛型類型的多態(tài)性,會生成一個橋接方法。

class MyNode extends Node {

    // 編譯器生成的橋接方法
    public void setData(Object data) {
        setData((Integer) data);
    }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }

    // ...
}

在擦除后,Node 的方法變成 setData(Object),MyNode 的方法變成了 setData(Integer),為了覆蓋 Node 的方法,編譯器在 MyNode 生成了一個 public void setData(Object data) 的橋接方法,這也是導(dǎo)致問題的原因.






Type Erasure


相關(guān)文章:

泛型:為什么使用泛型與泛型的基本使用
泛型:邊界和通配符
泛型:類型擦除

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容