论坛首页 Java企业应用论坛

关于Java的“双分派”

浏览 8786 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2013-06-02   最后修改:2013-06-02
最近在看设计模式的时候,在访问者模式中有提到一个双分派 "double dispatch"的问题。
其实单分派和双分派就是静态绑定和动态绑定的意思,搞个这么奇怪的名字,囧。
先看下几个类图定义:

角色的定义
public interface Role {
}

public class UglyRole implements Role {
}

public class BeautyRole implements Role {
}



演员的定义
public class AbsActor {
    // 演员能演一个角色
    public void act(Role role) {
        System.out.println("演员都可以演任何的角色");
    }

    public void act(BeautyRole beautyRole) {
        System.out.println("演员都可以演漂亮性感的角色");
    }
}

public class BeautyActor extends AbsActor {
    @Override
    public void act(BeautyRole beautyRole) {
        System.out.println("我最喜欢演性感角色了,哈哈");
    }
}

public class UglyActor extends AbsActor {
    public void act(BeautyRole beautyRole) {
        System.out.println("我很丑,所以我演不了性感角色");
    }
}


最后是测试类:
public class Client {
    public static void main(String[] args) {
        // 定义一个演员
        AbsActor actor = new UglyActor();
        // 定义一个角色
        Role role = new BeautyRole();
        // 开始演戏
        actor.act(role);
        // 动态绑定
        actor.act(new BeautyRole());
    }
}


输出结果:
演员都可以演任何的角色
我很丑,所以我演不了性感角色

Process finished with exit code 0


为了解决这个问题,引入了访问者模式,具体代码可以参考我的博客文章:http://yidao620c.iteye.com/blog/1881160
这样一来,不管演员类和角色类怎么变化,我们都能找到期望额方法执行,这就是双分派。可以看出Java是一个支持双分派的单分派语言(此话怎讲??)

书上面的解释是这么一段话:
重载在编译器就决定了要调用哪个方法,它是根据role的表面类型而决定调用act(Role role)方法,这是静态绑定;而Actor的执行方法act则是实际由其实际类型决定的,这是动态绑定。

这我就纳闷了,无论是是编译器决定还是实际类型决定,都得在运行期间才能运行吧,那运行期间的role其实类型是BeautyRole的,为什么执行结果会跑到抽象类中的方法中去呢? 

然后我又翻开了那块大砖头《Thinking In Java》,在多态那一章有这么一段话:
Java中除了static方法和final方法(private方法属于final方法)之外,其他所有方法都是后期绑定,也就是运行时绑定,我们不必判断是否应该进行后期绑定-它会自动发生。

这里面说的绑定是指的将一个方法调用同一个方法主体关联起来。而方法的重载是指我们在类中通过定义另外的相同方法名和不同的参数而使得一个类有很多相同方法名的方法,但是参数不一样,不管参数里面的类是继承关系还是啥关系都没有,方法是不一样的。动态绑定是只我们要绑定哪个真实对象的方法,比如上面的actor就在运行时绑定了UglyActor这个类的方法,那么这个类有几个重载的act方法,那么我找的究竟是哪个呢?你传的参数类型是啥就是啥,这个参数类型是编译器根据传入的参数的引用类型决定的,是Role就调用act(Role)方法,是BeautyRole就调用act(BeanutyRole)方法,这个找到方法的过程跟你运行时实际对象类型没有半毛钱的关系,这个是编译器就决定了的。

额,说了这么多,总之把之前对于多态和动态绑定的东西理了一遍,我也不知道自己这样的解释正不正确。希望大家讨论下,有什么不对的地方望指正。
   发表时间:2013-06-02  
不错,又复习了一般访问者模式.感觉访问者模式就是用方法重载的特性代替instanceof以及职责分离,instanceof一般是运行时判断的特性,访问者是编译期就决定的,这和你说的编译期绑定是一样道理
0 请登录后投票
   发表时间:2013-06-02  
holyselina 写道
不错,又复习了一般访问者模式.感觉访问者模式就是用方法重载的特性代替instanceof以及职责分离,instanceof一般是运行时判断的特性,访问者是编译期就决定的,这和你说的编译期绑定是一样道理


嗯,差不多可以这样理解。不过“可以看出Java是一个支持双分派的单分派语言”,这句话好诡异。
0 请登录后投票
   发表时间:2013-06-03  
静态单分派,动态单分派
《深入理解Java虚拟机》P209~P219讲的比较底层、清晰
0 请登录后投票
   发表时间:2013-06-04  
能不能这样理解。感觉是java方法绑定很丑陋的地方,还是要深入学习。。
0 请登录后投票
   发表时间:2013-06-04  
这个确实是非常蛋疼的地方,所以建议最好不要这样写,比如两个方法的重载,如果一个的参数是int,一个是Integer,那么最终会调用哪个?
0 请登录后投票
   发表时间:2013-06-05  
java语言静态多分派,动态单分派
0 请登录后投票
   发表时间:2013-06-05  
深入理解Java虚拟机
0 请登录后投票
   发表时间:2013-06-06  
holyselina 写道
不错,又复习了一般访问者模式.感觉访问者模式就是用方法重载的特性代替instanceof以及职责分离,instanceof一般是运行时判断的特性,访问者是编译期就决定的,这和你说的编译期绑定是一样道理


JVM 好像不是这样想的。
0 请登录后投票
   发表时间:2013-06-06  
瓶鱼跃 写道
这个确实是非常蛋疼的地方,所以建议最好不要这样写,比如两个方法的重载,如果一个的参数是int,一个是Integer,那么最终会调用哪个?

这个……依旧是看参数类型吧,如果参数直接是整形数的话,调用int的。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics