1. 程式人生 > >泛型中的型別擦除和橋方法

泛型中的型別擦除和橋方法

    在Java中,泛型的引入是為了在編譯時提供強型別檢查和支援泛型程式設計。為了實現泛型,Java編譯器應用型別擦除實現:

       1、  用型別引數(type parameters)的限定(如果沒有就用Object)替換泛型型別中的所有型別引數。

       2、  需要保持型別安全的時候插入型別轉換(隱含插入)

       3、  在extened 泛型型別中生成橋方法來保證多型性

   型別擦除確保不會為已引數化了的型別(paramterized types)產生新類,這樣泛型能保證沒有執行時的負載。

泛型型別擦除

      在型別擦除過程中,java編譯器擦除所有型別引數,用它的限定或者Object(沒限定時)替換。

     考慮下面的泛型類:

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; }
    // ...
}

因為型別引數T是非限定的,Java編譯器使用Object替換它:

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; }
    // ...
}

編譯器會使用第一個限定類,Comparable替換限定引數型別T:

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; }
    // ...
}

同樣,泛型方法也可以擦除。規則類似,不細說。

型別擦除的影響和橋方法

有時候型別擦除會引起無法預知的情況。比如:

給定以下兩個類:

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);
    }
}
考慮以下程式碼:
 MyNode mn = new MyNode(5);
Node n = mn;            // 原生型別 – 編譯器會給出未檢查警告
n.setData("Hello");     
Integer x = mn.data;    // 會引發丟擲ClassCastException 

型別擦除後,程式碼變成:

MyNode mn = new MyNode(5);
Node n = (MyNode)mn;         //原生型別 – 編譯器會給出未檢查警告
n.setData("Hello");
Integer x = (String)mn.data; //會引發丟擲ClassCastException
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);
    }
}

型別擦除後,方法的簽名已經不匹配。Node 方法變成setData(Object),MyNode方法變成setData(Integer)。MyNode setData方法已經不是覆蓋Node setData方法。

為了解決這個問題,維持泛型型別的多型性,java編譯器會生成一個橋方法:

 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);
    }

    // ...
}



原文:https://docs.oracle.com/javase/tutorial/java/generics/erasure.html