Java静态分派、动态分派、双分派

Java静态分派、动态分派、双分派

动态分派

介绍

public class Animal {
          
   
    public void execute() {
          
   
        System.out.println("animal");
    }
}

public class Dog extends Animal {
          
   
    public void execute() {
          
   
        System.out.println("dog");
    }
}

public class Cat extends Animal {
          
   
    public void execute() {
          
   
        System.out.println("cat");
    }
}

关于上面这段代码,如果我们执行

public class Client {
          
   
    public static void main(String[] args) {
          
   
        Animal a = new Animal();
        Animal c = new Cat();
        Animal d = new Dog();
        
        a.execute();
        c.execute();
        d.execute();
    }
}

显而易见,结果一定是 animal dog cat

原因

Java编译器在编译时期并不总是知道哪些代码会被执行,因为编译器仅仅知道对象的静态类型,不知道对象的真实类型;而方法调用则是根据对象的真实类型,而不是静态类型。

静态分派

介绍

public class Execute {
          
   
    public void execute(Animal animal) {
          
   
        System.out.println("animal");
    }

    public void execute(Dog dog) {
          
   
        System.out.println("dog");
    }

    public void execute(Cat cat) {
          
   
        System.out.println("cat");
    }
}

public class Animal {
          
   }

public class Dog extends Animal {
          
   }

public class Cat extends Animal {
          
   }

这段代码我们执行

public class Client {
          
   
    public static void main(String[] args) {
          
   
        Animal a = new Animal();
        Animal c = new Cat();
        Animal d = new Dog();

        Execute exe = new Execute();
        exe.execute(a);
        exe.execute(c);
        exe.execute(d);
    }
}

运行结果为

很多人可能疑惑这为什么跟我想的不一样?

原因

此处调用的是重载的方法,重载方法的分派是根据静态类型进行的,这个过程在编译时期就完成了。

双分派

介绍

有时候我们希望方法重载也能实现动态绑定,即根据传入参数实际类型调用相应方法

// Execute类同上
public class Execute {
          
   
    public void execute(Animal animal) {
          
   
        System.out.println("animal");
    }

    public void execute(Dog dog) {
          
   
        System.out.println("dog");
    }

    public void execute(Cat cat) {
          
   
        System.out.println("cat");
    }
}

public class Animal {
          
   
    public void accept(Execute exe) {
          
   
        exe.execute(this);
    }
}

public class Cat extends Animal{
          
   
    public void accept(Execute exe) {
          
   
        exe.execute(this);
    }
}

public class Dog extends Animal{
          
   
    public void accept(Execute exe) {
          
   
        exe.execute(this);
    }
}

运行如下代码

public class Client {
          
   
    public static void main(String[] args) {
          
   
        Animal a = new Animal();
        Animal c = new Cat();
        Animal d = new Dog();

        Execute exe = new Execute();
        a.accept(exe);
        c.accept(exe);
        d.accept(exe);
    }
}

结果如下

原因

前三个不用说了,静态分派。

后三个我们调用的时候将Execute对象传给重写过的accept方法,完成第一次分派,这里是动态分派,所以执行实际类型的方法,同时将自己this作为参数传递给exe.execute方法,完成第二次分派,这里Execute类有多个重载的方法,传递的是this(实际类型对象),从而实现方法的动态绑定。

双分派实现动态绑定的本质就是在重载方法委派的前面加上了继承体系中覆盖的环节,由于覆盖是动态的,所以重载就是动态的了

经验分享 程序员 微信小程序 职场和发展