分派Dispatch
不畏浮云遮望眼,只缘身在最高层。
一、Java动态绑定与双分派
从一个看过访问者模式的学习者的角度,为了更好的理解访问者设计模式,最好先要了解双分派是什么。
参考一篇让我茅塞顿开的文章:https://www.cnblogs.com/liaokailin/p/3804437.html
1.1动态绑定
动态绑定指程执行期间(而不是在编译期间)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法 。
如下代码:
package com.dp.zhb;
public class DynamicBound {
public static void main(String[] args) {
Person person = new Man();
person.say();
}
}
class Person {
public void say() {
}
;
}
class Man extends Person {
public void say() {
System.out.println("Hey Man");
}
}
运行结果:
调用的是Person对象中的say方法 但是实际执行的是Man中的方法,这就是动态绑定。
1.2静态绑定
静态绑定就是指在编译期就已经确定执行哪一个方法。方法的重载(方法名相同而参数不同)就是静态绑定的,重载时,执行哪一个方法在编译期就已经确定下来。
package com.dp.zhb;
public class StaticBound {
public static void main(String[] args) {
OutputName out = new OutputName();
Person p = new Person();
Person man = new Man();
Person woman = new Woman();
out.print(p);
out.print(man);
out.print(woman);
}
}
class Person {
}
class Man extends Person {
}
class Woman extends Person {
}
class OutputName {
void print(Person p) {
System.out.println("person");
}
void print(Man m) {
System.out.println("man");
}
void print(Woman w) {
System.out.println("woman");
}
}
上面这个程序输出结果:
不管在运行的时候传入的实际类型是什么,它永远都只会执行 void print(Person p)这个方法,即 : 重载是静态绑定的。
如果希望使用重载的时候,程序能够根据传入参数的实际类型动态地调用相应的方法,只能通过instanceof操作符进行类型的判断,然后再进行调用。虽然可以解决问题,但是如果子类数目很多,那么就要写很过个if else来判断类型,显然不是这种解决方案不是很合适。上述代码如下:
package com.dp.zhb;
public class StaticBound {
public static void main(String[] args) {
OutputName out = new OutputName() ;
Person p = new Person() ;
Person man = new Man() ;
Person woman = new Woman() ;
out.print(p) ;
out.print(man) ;
out.print(woman) ;
}
}
class Person{
}
class Man extends Person{
}
class Woman extends Person{
}
class OutputName{
void print(Person p){
if(p instanceof Man) print((Man)p);
else if (p instanceof Woman) print((Woman)p);
else System.out.println("person");
}
void print(Man m){
System.out.println("man");
}
void print(Woman w){
System.out.println("woman");
}
}
结果:
1.3 双分派
分派( dispatch)是指运行环境按照对象的实际类型为其绑定对应方法体的过程。
double dispatch(双分派)在选择一个方法的时候,不仅仅要根据消息接收者(receiver) 的运行时型别(Run time type),还要根据参数的运行时型别(Run time type)。这里的消息接收者其实就是方法的调用者。具体来讲就是,对于消息表达式a.m(b) ,双分派能够按照a和b的实际类型为其绑定对应方法体。
来看一个双分派的例子:
package com.dp.zhb;
class Father {
public void accept(Execute exe) {
exe.method(this);
}
}
class Son1 extends Father {
@Override
public void accept(Execute exe) {
exe.method(this);
}
}
class Son2 extends Father {
@Override
public void accept(Execute exe) {
exe.method(this);
}
}
class Execute {
public void method(Father father) {
System.out.println("This is Father's method");
}
public void method(Son1 son) {
System.out.println("This is Son1's method");
}
public void method(Son2 son) {
System.out.println("This is Son2's method");
}
}
public class Test {
public static void main(String[] args) {
Father father = new Father();
Father s1 = new Son1();
Father s2 = new Son2();
Execute exe = new Execute();
father.accept(exe);
s1.accept(exe);
s2.accept(exe);
}
}
运行结果:
通俗的解释一下,就是重载是静态绑定,重写是动态绑定,双分派把重写放在重载之前,以实现在运行时动态判断执行那个子类的方法。上面的例子中,首先依据重写(Java的多态)找到对应的accept方法,然后accept方法中调用method方法, 并把当前类的this传入method,传入this这步就属于是静态绑定,在编译器就确定好的。比如在Father类中:
class Father {
public void accept(Execute exe) {
exe.method(this);
}
}
method(this)就对应了Execute类中的
public void method(Father father){
System.out.println("This is Father's method");
}
版权声明:如无特别声明,本站收集的文章归 HuaJi66/Others 所有。 如有侵权,请联系删除。
联系邮箱: GenshinTimeStamp@outlook.com
本文标题:《 分派Dispatch 》
本文链接:/%E8%AE%BE%E8%AE%A1%E6%80%9D%E6%83%B3/%E5%88%86%E6%B4%BEDispatch.html