Java's Dynamic Method Selection Explained

This article is fairly under-construction...

Java's dynamic method selection is painful to understand if you don't get its design methodology.

We're gonna go through a few simple code snippets to make sure we can understand it with keeping some really simple principles in mind.

Suppose we have the following interface and class implementation:

public interface Animal {
    default void greet(Animal a) {
        System.out.println("greet :: Animal!");
    }

    default void sniff(Animal a) {
        System.out.println("sniff :: Animal!");
    }

    default void flatter(Animal a) {
        System.out.println("flatter :: Animal!");
    }
}

public class Dog implements Animal {
    public void sniff(Animal a) {
        System.out.println("sniff :: Dog!");
    }

    public void flatter(Dog d) {
        System.out.println("flatter :: Dog!");
    }
}

Let's assume we have the following lines of code in a valid main function that can be run directly. What is the expected output for the program?

Animal a = new Dog();
Dog d = new Dog();
a.greet(d);

It should be not hard for you to convince yourself a line with greet :: Animal! will be printed out. OK, what about the following line?

d.flatter(d);
d.flatter(a);

The answer is

flatter :: Dog!
flatter :: Animal!

You should be able to notice that the flatter method in class Dog is actually overloading the flatter method in interface Animal rather than overriding it (the method signatures are different!). But how does Java handle all these complicated things?

Be relaxed, let's understand and remember the following principles:

  • If we have a variable has static type X and dynamic type Y, then if Y overrides the method, Y's method is used instead.
  • At compile time, the compiler will verify X has a method that can handle the given parameter. If multiple methods can handle, it records the most specific one.
  • At runtime, if Y overrides the recorded signature, use the overridden method.

We provide a more concrete example and comments for a better understanding of these principles.

public class Main {
    public static void main(String[] args) {
        // static: Animal
        // dynamic: Dog
        Animal a = new Dog();
        // static: Dog
        // dynamic: Dog
        Dog d = new Dog();

        // compile: Animal's `greet(Animal a)` is recorded
        // run: not found Dog's `greet(Animal a)`
        // result: Animal.greet
        a.greet(d);
        // compile: Animal's `sniff(Animal a)` is recorded
        // run: found Dog's `sniff(Animal a)`
        // result: Dog.sniff
        a.sniff(d);
        // compile: Dog's `sniff(Animal a)` is recorded
        // run: keep Dog's `sniff(Animal a)`
        // result: Dog.sniff
        d.sniff(a);
        // compile: Animal's flatter(Animal a) is recorded
        // run: not found Dog's flatter(Animal a)
        // result: Animal.flatter
        a.flatter(d);
        // compile: Animal's flatter(Animal a) is recorded
        // run: not found Dog's flatter(Animal a)
        // result: Animal.flatter
        a.flatter(a);
        // compile: Dog's flatter(Dog d) is recorded
        // run: keep Dog's flatter(Dog d)
        // result: Dog.flatter
        d.flatter(d);
        // compile: Animal's flatter(Animal a) is recorded,
        // as Dog does have a flatter method but not with the same signature.
        // run: not found Dog's flatter(Animal a)
        // result: Animal.flatter
        d.flatter(a);
    }
}

It worth to note that although in the example above Animal is an interface, the exact same idea can be applied for extends as well. Another example is provided here for a practice purpose.

public class Main {
    public static void main(String[] args) {
        // static: Bird
        // dynamic: Falcon
        Bird bird = new Falcon();
        // static: Falcon
        // dynamic: Falcon
        Falcon falcon = (Falcon) bird;

        // compile: Bird's `gulgate(Bird b)` is recorded
        // run: not found Falcon's `gulgate(Bird b)`
        // result: Bird.gulgate
        bird.gulgate(bird);

        // compile: Bird's `gulgate(Bird b)` is recorded
        // run: not found Falcon's `gulgate(Bird b)`
        // result: Bird.gulgate
        bird.gulgate(falcon);

        // compile: Bird's `gulgate(Bird b)` is recorded
        // as Falcon does have a gulgate method but not with the same signature.
        // run: not found Falcon's `gulgate(Bird b)`
        // result: Bird.gulgate
        falcon.gulgate(bird);

        // compile: Falcon's `gulgate(Falcon f)` is recorded
        // run: keep Falcon's `gulgate(Falcon f)`
        // result: Falcon.gulgate
        falcon.gulgate(falcon);
    }
}


class Bird {
    public void gulgate(Bird b) {
        System.out.println("Bird Gul!");
    }
}

class Falcon extends Bird {
    public void gulgate(Falcon f) {
        System.out.println("Falcon Gul!");
    }
}

Reference

最后編輯于
?著作權(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ù)。

相關(guān)閱讀更多精彩內(nèi)容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,872評論 0 10
  • 剛開始鼓勵孩子們堅持寫作文時,給孩子們許諾,堅持一百天我有獎勵,說實話,我沒想到孩子們能堅持下來,這對于五年...
    行走的教育閱讀 468評論 0 8
  • 材料:丹可林重彩油畫棒?ω?剛?cè)胧值?用時:一個鐘 以下為部分步驟圖?ω?: 《醉夕》 ——拾穗 夕陽...
    拾穗的時光閱讀 535評論 3 9
  • 已經(jīng)記不清這是本月的第幾次酒醉歸來,再三保證的很快就會回來,在你父親的一聲又是十點中你嘟囔一句不會的,一溜...
    七月的桃之妖妖閱讀 397評論 0 1
  • 和昨天估計的一樣,今天的體重略有回升。早上,和幾位同樣在減重的朋友交流經(jīng)驗,發(fā)現(xiàn)一個奇怪的現(xiàn)象。有兩個體重和身高和...
    花花世界草草人生閱讀 1,331評論 0 4

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