JAVA動態綁定淺析

動態綁定

動態綁定做爲Java多態特性的一個重要實現html

有如下三個類:java

public class Fruit {
    public static final String TAG = "Fruit";

    public Fruit() {
    }

    public void say() {
        Log.i(TAG, "say: i am fruit: ");
    }
}
public class Apple  extends  Fruit{

    public void say(){
        Log.i(TAG, "say:  i am apple");
    }
}
public class Orange extends Fruit {

    public void say(){
        Log.i(TAG, "say:  i am orange");
    }
}

其中Apple和Orange類都繼承Fruit類。app

而後在使用的時候ui

Fruit fruit = new Apple();
fruit.say();

咱們都知道最後打印的結果是debug

debug I/Fruit: say:  i am apple

很明顯這就是Java多態特性的體現。日誌

咱們明明調用的是Fruit對象的方法。可是運行時卻調用了Apple對象的方法,這是怎麼實現的呢?這就涉及到了Java的動態綁定了。code

這裏肯定兩個概念:編譯時類型與運行時類型htm

編譯時類型就是指改對象在編譯後的文件裏的類型也就是該對象聲明時的類型,而運行時類型是指在程序運行時動態指定的類型也就是該對象定義時的類型。若是編譯時類型與運行時類型不一致就會發生運行時動態綁定。對象

好比上面定義的Fruit fruit = new Apple();blog

fruit的編譯時類型是Fruit。

咱們能夠從編譯後的class文件看出

#4 = Class              #243          // com/meiliwu/dragon/model/Apple
#5 = Methodref          #4.#241       // com/meiliwu/dragon/model/Apple."<init>":()V
#6 = Methodref          #244.#245     // com/meiliwu/dragon/model/Fruit.say:()V

咱們能夠從編譯後的文件看出。fruit.say方法在編譯後指向的是Fruit的say方法。可是運行時類型是Apple。運行時倒是調用Apple的say方法。咱們從日誌能夠看出來。

若是咱們將代碼改爲這樣

Apple fruit = new Apple();
fruit.say();

打印日誌以下:

12-25 11:25:17.803 9709-9709/com.meiliwu.dragon.debug I/Fruit: say:  i am apple

咱們再看看編譯後的文件

4 = Class              #243          // com/meiliwu/dragon/model/Apple
#5 = Methodref          #4.#241       // com/meiliwu/dragon/model/Apple."<init>":()V
#6 = Methodref          #4.#244       // com/meiliwu/dragon/model/Apple.say:()V

從代碼能夠看出編譯時類型與運行時類型一致,這是不會發生動態綁定的,這時能夠從編譯後的class文件得出驗證。

JAVA的PECS原則

PECS指「Producer Extends,Consumer Super」

好比:

List<? extends Fruit> fruitList = new ArrayList<>();
fruitList.add(new Apple());
fruitList.add(new Orange());

可是編譯器卻報了編譯錯誤:

按理說fruitList是一個持有類型爲Fruit及其子類的泛型列表啊,爲何不能往其中添加Fruit的子類呢?

由於泛型的一大好處就是能夠在編譯時檢查,避免傳入不相符的類型可能致使的ClassCastException了。可是聲明fruitList的時候沒有明確的指定泛型的具體類型,因此編譯器沒法確認其持有的具體類型,固然也就拒絕了add操做。

fruitList只是規定了泛型的上限,可是並無肯定具體的類型,也沒法肯定具體的子類型,能夠是Apple,Orange還多是Banana,因此不能把具體的對象添加進去,否則使用的時候可能致使ClassCastException了。可是能夠保證從裏面取出來的數據都是Fruit及其子類,並且仍是Fruit的某一個子類。

咱們把代碼改爲下面這樣子就能夠添加不一樣的Fruit對象了。

由於咱們規定了fruitList持有的都是Fruit及其父類,能夠將Fruit 及其子類都添加進去

List<? super Fruit> fruitList = new ArrayList<>();
fruitList.add(new Apple());
fruitList.add(new Orange());

可是

fruitList.add(new Object());

卻不行,why?由於在編譯時沒法確認具體的fruitList持有的是Fruit的哪個父類,要想肯定就必須指定具體泛型類了。因此就沒法往裏面寫入Fruit的父類型對象。

因此咱們能夠從Java Collctions copy方法的簽名能夠看出

public static <T> void copy(List<? super T> dest, List<? extends T> src) {

dest集合持有?super T ,能夠往裏面寫入全部T及其子類對象,而src集合持有? extends T泛型。能夠確保的是從裏面讀取的數據都是T及其子類。因此能夠寫入dest了。

PS:Java 類的final方法和static不能複寫

Reference

1:https://www.cnblogs.com/ygj09...

2: http://www.importnew.com/8966...

相關文章
相關標籤/搜索