动态分派和静态分派机制是java多态的实现原理。
静态分派(方法重载)
静态分派机制最典型的代码:
void test() {
Father father = new Son(); //静态分派
print(father);
}
void print(Father father) {
System.out.println("this is father");
}
void print(Son son) {
System.out.println("this is son");
}
这段代码执行完会输出this is father.之所以是这个结果,原因是此处的多态是静态分派。对于方法的重载。在编译阶段由于father变量被声明为Father类型。在编一阶段就已经确定了调用的是参数为Father的方法,与具体的实例化对象无关。
动态分派
动态分派机制最典型的代码:
void test() {
Father father = new Son(); //静态分派
father.name(); //动态分派
}
class Son extends Father {
void name(){
System.out.println("son");
}
}
class Father {
void name(){
System.out.println("father");
}
}
这里我们声明了静态类型Father,但是实际上我们调用name方法的时候,输出的却是son。对于方法的重写,java采用的是动态分派的机制。也就是说在运行时才确定调用那个方法。由于father的实际类型是Son,因此会调用Son的name方法。
动态分派只会体现在方法的调用者身上,而方法的参数类型则会在编译器由编译器确定。
在Java多态的两种常见用法中,方法重载使用的是静态分派机制,而方法重写使用的是静态分派机制。这也就导致了,方法重载调用的时候是根据变量的静态类型来决定调用哪个方法。而方法重写的时候,则是根据变量的实际类型来决定调用哪个方法
双重分派
如果参数类型(静态分派的类型)能够在运行期绑定,那么具体调用的是哪个方法就可以由方法参数和方法调用者共同决定。
既然方法的调用者类型是运行期才确定的,那么我们使方法的参数也在运行期去确定就可以控制具体调用哪个方法了。
那么如何才能将方法参数在运行期确定呢?
使用this就能运行期确定参数类型
accept(Father arg) {
arg.print(this);
}
至此,就是双重分派的含义。哪个print被调用最终经过两次动态分派才被确定下来。
arg.print方法 这是一次动态分派。
而this 也是运行期确定下来的,这是第二次动态分派。
说完了双重分派 我们在说到visitor模式上,visitor模式的核心就是双重分派。此时print方法就变成了跨对象的操作方法。(任何Father的子类都会被识别出来)