1. 程式人生 > >java動態/靜態繫結以及雙分派

java動態/靜態繫結以及雙分派

動態繫結:程式在執行期間而不是在編譯期間,根據所引用物件的實際型別呼叫對應的方法,重寫以及介面的實現都屬於該範疇,使用實際的物件資訊來完成API的呼叫

public class DynamicBind {

    public static void main(String[] args) {
        Father f = new Father();
        f.method();

        f = new Son();
        f.method();

        f = new Daughter();
        f.method();
    }
}

class Father {
    public void method() {
        System.out.println("Father.method");
    }
}

class Son extends Father{
    public void method() {
        System.out.println("Son.method");
    }
}

class Daughter extends Father{
    public void method() {
        System.out.println("Daughter.method");
    }
}
output
Father.method
Son.method
Daughter.method

儘管引用的型別都是Father,但是在執行時卻呼叫的是實際型別的方法,也就是說父類引用指向子類例項時,會根據實際例項型別呼叫對應的方法

靜態繫結:在編譯期間已經確定執行的方法,API的過載屬於該範疇,也就是說需要執行的方法在編譯期間已經確定。凡是使用staticprivatefinal修飾的方法或者屬性也屬於該範疇,static修飾的方法或者屬性屬於類,不能被繼承也就是說無法重寫。private只有該類的物件能夠訪問,不能被繼承也就是說無法重寫,final修改的屬性不允許重新複製,可以理解為常量,final

修飾的方法不允許被重寫。使用類資訊完成API的呼叫

public class StaticBind {
    public static void main(String[] args) {
        Ext ext = new Ext();

        Mother m = new Mother();
        ext.method(m);

        m = new Tom();
        ext.method(m);

        m = new Mark();
        ext.method(m);

    }
}

class Mother{}
class Tom extends Mother{}
class Mark extends Mother{}

class Ext {
    public void method(Mother mother) {
        System.out.println("Mother.method");
    }

    public void method(Tom tom) {
        System.out.println("Tom.method");
    }

    public void method(Mark mark) {
        System.out.println("Mark.method");
    }
}
output
Mother.method
Mother.method
Mother.method

編譯時通過方法簽名已經確定呼叫的方法,也就是說在實際呼叫中傳入了不同的物件資訊,但是實際的類資訊只有一個就是Mather


偽動態實現(instanceof採用型別判斷的方式實現過載的動態效果

class Ext {
    public void method(Mother mother) {
        if (mother instanceof Tom) {
            System.out.println("Tom.method");
        } else if (mother instanceof Mark) {
            System.out.println("Mark.method");
        } else if (mother instanceof Mother) {
            System.out.println("Mother.method");
        }
    }

    public void method(Tom tom) {
        System.out.println("Tom.method");
    }

    public void method(Mark mark) {
        System.out.println("Mark.method");
    }
}
父類的判斷必須要寫在最後,這個後和異常捕獲的道理時一致的,如果將父類寫在最前邊,那麼和之前的執行效果時一致的,因為自類和父類是is-a的關係。雙分派實現動態繫結
public class DispatchBind {
    public static void main(String[] args){
        Execute exe = new Execute();

        Mom mom = new Mom();
        mom.accept(exe);

        mom = new Jack();
        mom.accept(exe);


        mom = new Licy();
        mom.accept(exe);
    }
}

class Mom{
    public void accept(Execute execute) {
        execute.method(this);
    }
}

class Jack extends Mom{
    public void accept(Execute execute) {
        execute.method(this);
    }
}

class Licy extends Mom{
    public void accept(Execute execute) {
        execute.method(this);
    }
}

class Execute {
    public void method(Mom Mom) {
        System.out.println("Mom.method");
    }

    public void method(Jack jack) {
        System.out.println("Jack.method");
    }

    public void method(Licy licy) {
        System.out.println("Licy.method");
    }
}
output
Mom.method
Jack.method
Licy.method
雙分派實現動態繫結的本質,就是在過載方法委派的前面加上了繼承體系中重寫的環節,由於重寫是動態的,所以過載就是動態的了

第一次分派:mom = new Jack();mom.accpet(ext);父類引用指向自類例項,並且自類重寫了父類方法所有會呼叫自類Jack的accpet()方法

第二次分派:發生在Jack類中的accpet中,關鍵子為this。該處的this可以理解為Jack.this.此時會呼叫Execute中的method(Jack jack)

雙分派意味著請求的操作取決於請求的種類和接受者的型別