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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,490评论 0 10
  • 刚开始鼓励孩子们坚持写作文时,给孩子们许诺,坚持一百天我有奖励,说实话,我没想到孩子们能坚持下来,这对于五年...
    行走的教育阅读 446评论 0 8
  • 材料:丹可林重彩油画棒✪ω✪刚入手的 用时:一个钟 以下为部分步骤图✪ω✪: 《醉夕》 ——拾穗 夕阳...
    拾穗的时光阅读 459评论 3 9
  • 已经记不清这是本月的第几次酒醉归来,再三保证的很快就会回来,在你父亲的一声又是十点中你嘟囔一句不会的,一溜...
    七月的桃之妖妖阅读 361评论 0 1
  • 和昨天估计的一样,今天的体重略有回升。早上,和几位同样在减重的朋友交流经验,发现一个奇怪的现象。有两个体重和身高和...
    花花世界草草人生阅读 1,177评论 0 4