- 浏览: 326051 次
- 性别:
- 来自: 广州
文章分类
最新评论
一:分派的概念
变量被声明时的类型叫做变量的静态类型,或叫做明显类型,而变量所引用的对象的真实类型叫做实际类型,这种根据对象的类型而对方法进行的选择,就是分派.分派是面向对象的语言所提供的关键特性之一,根据分派发生的时期
,分为静态分派和动态分派.
静态分派发生在编译时期,分派根据静态类型信息发生,如方法重载就是静态分派
动态分派发生在运行时期,动态分派动态地转换掉某个方法,面向对象的语言利用动态分派来实现方法转换产生多态性.
(a)方法重载----墨子骑马
显然Mozi类的ride方法是由三个方法重载而成的,这三个方法分别接收马,白马,黑马。两次调用ride方法输出的都
是Riding a horse.想下,两次对ride方法的调用传入的是不同的参量,也就是wh和bh,它们虽然具有不同的真实类
型,但是它们的明显类型都是House类型。重载方法的分派是根据静态类型进行的,这个分派过程在编译时期就完成
了.这就是静态分派的例子.
(b)动态分派--Java通过方法的转换(Overriding)支持动态分派
请看下面的代码:
上面的例子中,变量s1和s的静态类型和真实类型都是String,而o的静态类型是Object,真实类型则是Object类型的
一个子类型。分析:
如果上面最后一行的equals方法调用的是String类的equals方法,那么上面代码检查的就是o的值是否博字符串"abc"
,相反,如果上面的equals方法调用的是Object类的equals方法,那么检查的就是o所指的对象和s1所指的对象是不
是同一个对象.
所以问题的核心是Java编译器在编译时期并不总是知道哪一些代码会被执行,因为编译器仅仅知道对象的静态类型,
而不知道对象的真实类型,而方法的调用则是根据真实类型(o的真实类型为String),而不是静态类型。变量o指向一
个类型为String的对象,这个String对象的值是"abc",这样一来,一面最后一行的equals方法调用的是String类的
equals方法,而不是Object类的equals方法.
二:分派的类型
一个方法所属的对象叫做方法的接收者,方法的接收者与方法的参量统称为方法的宗量.根据分派可以基于多少种宗
量,可以将面向对象的语言分为单分派语言和多分派语言.单分派语言根据一个宗量的类型进行对方法的选择,多分
派语言根据多于一个宗量的类型对方法进行选择.Java是单分派语言,因为它动态分派仅仅会考虑到方法的接收者类
型,同时又是静态的多分派语言,因为它对重载方法的分派会考虑到方法的接收者类型以及方法的所有参量的类型.
在一个支持动态单分派的语言里(如Java),有两个条件决定了一个请求会调用哪一个操作,一是请求的名字,二是接
收者的真实类型,单分派限制了方法的选择过程,使得只有一个宗量可以被考虑到,这个宗量通常就是方法的接收者
。总之,Java语言支持静态的多分派和动态的单会派.
三:一个静态分派和动态分派的例子
下面的例子说明了在Java语言中静态多分派和动态单分派是如何发生的:
从Client可以看出,由于方法的重载,对Point对象的两种translate方法的调用是典型的静态多分派,而对子类以及
父类的draw方法调用则是典型的动态单分派.
四:双重分派
一个方法根据两个宗量的类型来决定执行不同的代码,这就是"双分派"或者"双重分派",Java语言不支持动态的多分
派,但是通过设计模式也可以在Java语言里实现动态的双重分派.双重分派是多重分派的一种,双重分派是由连续的
两次单分派组成的。怎么样在Java语言中实现动态的双重分派呢?下面看下用"返传球"实现双重分派:
(a)返传球
仔细想想,既然Java语言支持动态的单分派,那么为什么不可以通过两次方法调用来达到两次分派的目的呢?假设有
两个类West和East,这两个类分别有两个方法,叫做goWest和goEast,现在west对象首先调用east对象的goEast方法
并将自己传入,在east对象被调用时,立即根据传入的参量知道调用者是谁,于是反过来调用调用者对象的goWest方
法,通过两次调用将程序控制权轮番交给两个对象,这样就出现了两次方法调用,程序的控制权被两个对象像传球一
样,首先由west对象传给了east对象,然后又被返传给了west对象。
(b)方法的置换
仅仅返传了一下球,并不能解决双重分派的问题,关键是怎么利用这两次调用,以及Java语言的动态单分派功能,使
得在这种传球过程中能够触发两次(动态)单分派.
动态单分派在Java语言中是在子类型将父类型的方法置换掉时发生的,换言之,West和East都必须分别置身于自己的
类型等级结构中.于是在这个系统里,有一个客户端,以及两个等级结构---West等级结构和East等级结构,在West等
级结构中,由West定义出公共的类型,并有两个具体子类实现了抽象类的接口,而East等级结构也与此相像.
系统运行时,会首先创建SubWest1和SubEast1对象,然后客户端调用SubEast1的goEast()方法,并将SubWest1对象传
入(由于SubEast1对象置换了其超类East的goEast方法,因此这个时候就发生了一次动态单分派)。当SubEast1对象接
到调用时,会从参数中得到SubWest1对象,所以它就立即调用这个对象的goWest1方法,并将自己传入(由于SubEast1
对象有权选择调用哪一个对象,因此在此时又进行一次动态的方法分派)
由output知道,两个名字一个来自East等级结构,一个来自West等级结构,困此它们的组合式是动态决定的,这就是
动态双重分派的实现机制.
最后需要指出的是,首先East和West等级结构的大小是彼此独立的,虽然在上面的例子中两者都包含了两个具体成员
,但这只是巧合。其次West要访问的East等级结构中的节点数目应当与West对象所配备的方法的数目相等,这是必然
的.
这种返传球的设计就是访问者模式的精华
变量被声明时的类型叫做变量的静态类型,或叫做明显类型,而变量所引用的对象的真实类型叫做实际类型,这种根据对象的类型而对方法进行的选择,就是分派.分派是面向对象的语言所提供的关键特性之一,根据分派发生的时期
,分为静态分派和动态分派.
静态分派发生在编译时期,分派根据静态类型信息发生,如方法重载就是静态分派
动态分派发生在运行时期,动态分派动态地转换掉某个方法,面向对象的语言利用动态分派来实现方法转换产生多态性.
(a)方法重载----墨子骑马
package cai.milenfan.basic.test; public abstract class Horse { }
package cai.milenfan.basic.test; public class BlackHorse extends Horse{ }
package cai.milenfan.basic.test; public class WhiteHorse extends Horse{ }
package cai.milenfan.basic.test; public class Mozi { private Horse lnkHorse; public void ride(Horse h) { System.out.println("Riding a horse"); } public void ride(WhiteHorse wh) { System.out.println("Riding a white horse"); } public void ride(BlackHorse bh) { System.out.println("Riding a black horse"); } public static void main(String[] args) { Horse wh = new WhiteHorse(); Horse bh = new BlackHorse(); Mozi mozi = new Mozi(); mozi.ride(wh); mozi.ride(bh); } }
显然Mozi类的ride方法是由三个方法重载而成的,这三个方法分别接收马,白马,黑马。两次调用ride方法输出的都
是Riding a horse.想下,两次对ride方法的调用传入的是不同的参量,也就是wh和bh,它们虽然具有不同的真实类
型,但是它们的明显类型都是House类型。重载方法的分派是根据静态类型进行的,这个分派过程在编译时期就完成
了.这就是静态分派的例子.
(b)动态分派--Java通过方法的转换(Overriding)支持动态分派
请看下面的代码:
package cai.milenfan.basic.test; public class DongTaiFenPai { public static void main(String[] args){ String s1 = "ab"; Object o = s1 "c"; String s = "abc"; boolean b = o.equals(s);//o的真实类型为String System.out.println(b); } }
上面的例子中,变量s1和s的静态类型和真实类型都是String,而o的静态类型是Object,真实类型则是Object类型的
一个子类型。分析:
如果上面最后一行的equals方法调用的是String类的equals方法,那么上面代码检查的就是o的值是否博字符串"abc"
,相反,如果上面的equals方法调用的是Object类的equals方法,那么检查的就是o所指的对象和s1所指的对象是不
是同一个对象.
所以问题的核心是Java编译器在编译时期并不总是知道哪一些代码会被执行,因为编译器仅仅知道对象的静态类型,
而不知道对象的真实类型,而方法的调用则是根据真实类型(o的真实类型为String),而不是静态类型。变量o指向一
个类型为String的对象,这个String对象的值是"abc",这样一来,一面最后一行的equals方法调用的是String类的
equals方法,而不是Object类的equals方法.
二:分派的类型
一个方法所属的对象叫做方法的接收者,方法的接收者与方法的参量统称为方法的宗量.根据分派可以基于多少种宗
量,可以将面向对象的语言分为单分派语言和多分派语言.单分派语言根据一个宗量的类型进行对方法的选择,多分
派语言根据多于一个宗量的类型对方法进行选择.Java是单分派语言,因为它动态分派仅仅会考虑到方法的接收者类
型,同时又是静态的多分派语言,因为它对重载方法的分派会考虑到方法的接收者类型以及方法的所有参量的类型.
在一个支持动态单分派的语言里(如Java),有两个条件决定了一个请求会调用哪一个操作,一是请求的名字,二是接
收者的真实类型,单分派限制了方法的选择过程,使得只有一个宗量可以被考虑到,这个宗量通常就是方法的接收者
。总之,Java语言支持静态的多分派和动态的单会派.
三:一个静态分派和动态分派的例子
下面的例子说明了在Java语言中静态多分派和动态单分派是如何发生的:
package cai.milenfan.basic.test; import java.awt.Canvas; public class Point { private int x; private int y; public Point() { } public void draw(Canvas c) { // write you code here } public void translate(int d) { x = d; y = d; } public void translate(int dx, int dy) { x = dx; y = dy; } }
package cai.milenfan.basic.test; import java.awt.Canvas; import java.awt.Color; public class ColorPoint extends Point { private Color c; public ColorPoint() { } public void draw(Canvas C) { // Color point code } }
package cai.milenfan.basic.test; import java.awt.Canvas; public class Client { private static Point p; private static Point pc; public static void main(String[] args) { p = new Point(); pc = new ColorPoint(); // static multi-dispatch p.translate(5); // one int version p.translate(1, 2); // two int version // dynamic uni-dispatch Canvas aCanvas = new Canvas(); p.draw(aCanvas); // Point.draw() pc.draw(aCanvas); // ColorPoint.draw(); } }
从Client可以看出,由于方法的重载,对Point对象的两种translate方法的调用是典型的静态多分派,而对子类以及
父类的draw方法调用则是典型的动态单分派.
四:双重分派
一个方法根据两个宗量的类型来决定执行不同的代码,这就是"双分派"或者"双重分派",Java语言不支持动态的多分
派,但是通过设计模式也可以在Java语言里实现动态的双重分派.双重分派是多重分派的一种,双重分派是由连续的
两次单分派组成的。怎么样在Java语言中实现动态的双重分派呢?下面看下用"返传球"实现双重分派:
(a)返传球
仔细想想,既然Java语言支持动态的单分派,那么为什么不可以通过两次方法调用来达到两次分派的目的呢?假设有
两个类West和East,这两个类分别有两个方法,叫做goWest和goEast,现在west对象首先调用east对象的goEast方法
并将自己传入,在east对象被调用时,立即根据传入的参量知道调用者是谁,于是反过来调用调用者对象的goWest方
法,通过两次调用将程序控制权轮番交给两个对象,这样就出现了两次方法调用,程序的控制权被两个对象像传球一
样,首先由west对象传给了east对象,然后又被返传给了west对象。
(b)方法的置换
仅仅返传了一下球,并不能解决双重分派的问题,关键是怎么利用这两次调用,以及Java语言的动态单分派功能,使
得在这种传球过程中能够触发两次(动态)单分派.
动态单分派在Java语言中是在子类型将父类型的方法置换掉时发生的,换言之,West和East都必须分别置身于自己的
类型等级结构中.于是在这个系统里,有一个客户端,以及两个等级结构---West等级结构和East等级结构,在West等
级结构中,由West定义出公共的类型,并有两个具体子类实现了抽象类的接口,而East等级结构也与此相像.
package cai.milenfan.basic.test; public abstract class East { public abstract void goEast(West west); }
package cai.milenfan.basic.test; public class SubEast1 extends East { public void goEast(West west) { west.goWest1(this); } public String myName1() { return "SubEast1"; } }
package cai.milenfan.basic.test; public class SubEast2 extends East { public void goEast(West west) { west.goWest2(this); } public String myName2() { return "SubEast2"; } }
package cai.milenfan.basic.test; public abstract class West { public abstract void goWest1(SubEast1 east); public abstract void goWest2(SubEast2 east); }
package cai.milenfan.basic.test; public class SubWest1 extends West { public void goWest1(SubEast1 east) { System.out.println("SubWest1 " east.myName1()); } public void goWest2(SubEast2 east) { System.out.println("SubWest1 " east.myName2()); } }
package cai.milenfan.basic.test; public class SubWest2 extends West { public void goWest1(SubEast1 east) { System.out.println("SubWest2 " east.myName1()); } public void goWest2(SubEast2 east) { System.out.println("SubWest2 " east.myName2()); } }
package cai.milenfan.basic.test; import java.awt.Canvas; public class Client { private static East east; private static West west; public static void main(String[] args) { // combination 1 east = new SubEast1(); west = new SubWest1(); east.goEast(west); // combination 2 east = new SubEast1(); west = new SubWest2(); east.goEast(west); } } output: SubWest1 SubEast1 SubWest2 SubEast1
系统运行时,会首先创建SubWest1和SubEast1对象,然后客户端调用SubEast1的goEast()方法,并将SubWest1对象传
入(由于SubEast1对象置换了其超类East的goEast方法,因此这个时候就发生了一次动态单分派)。当SubEast1对象接
到调用时,会从参数中得到SubWest1对象,所以它就立即调用这个对象的goWest1方法,并将自己传入(由于SubEast1
对象有权选择调用哪一个对象,因此在此时又进行一次动态的方法分派)
由output知道,两个名字一个来自East等级结构,一个来自West等级结构,困此它们的组合式是动态决定的,这就是
动态双重分派的实现机制.
最后需要指出的是,首先East和West等级结构的大小是彼此独立的,虽然在上面的例子中两者都包含了两个具体成员
,但这只是巧合。其次West要访问的East等级结构中的节点数目应当与West对象所配备的方法的数目相等,这是必然
的.
这种返传球的设计就是访问者模式的精华
发表评论
-
Mina重连
2014-05-26 21:29 2858import com.sun.swing.internal. ... -
面试经典
2014-05-24 09:29 6031.mysql innodb引擎,什么叫聚集索引,与非聚集索 ... -
一拍网网站系统架构图
2014-03-28 21:24 528一拍网网站系统架构图 -
Window下安装配置nginx
2013-08-12 16:53 729安装:http://www.cnblogs.com/wen ... -
使用线程池的好处
2013-07-18 14:41 1130使用线程池有两个好处: 1.可以创建和销毁线程所带来的系统 ... -
Java ThreadLocal使用浅析
2013-07-18 14:36 409ThreadLocal通过在其内部保存变量的副本,并且各个副本 ... -
MyBatis学习之简单增删改查操作、MyBatis存储过程、MyBatis分页、MyBatis一对一、MyBatis一对多
2013-07-05 13:06 1081http://blog.csdn.net/zhangwei ... -
分享一位网友的架构杂谈
2013-05-20 23:16 864不容类型的网站,并发处理不一样,例如针对sns这种类型的网站 ... -
JSP页面静态化
2013-04-08 09:20 794http://www.java-zone.org/644.ht ... -
Java compile to C++
2013-03-19 14:53 458http://code.google.com/a/eclips ... -
几个TCP Socket的通信框架
2013-03-19 12:26 938http://www.oschina.net/p/simple ... -
宝贝鱼
2013-03-18 23:54 629http://code.google.com/p/cshbbr ... -
将Java程序注册成系统服务(NT服务)
2013-03-16 16:14 564http://blog.csdn.net/small____f ... -
Java内存回收机制
2013-03-13 15:47 776http://www.iteye.com/blogs/tag/ ... -
支付宝,百付宝集成
2013-03-13 14:01 890http://help.alipay.com/support/ ... -
SSH+EXTJS项目下载
2013-03-11 23:02 382http://download.csdn.net/tag/Ex ... -
Hibernate中使用Threadlocal创建线程安全的Session
2013-03-04 20:39 484http://blog.sina.com.cn/s/blog_ ... -
Java Socket多线程通信
2012-10-09 09:53 806当Server没接受到一个Client连接请求之后,都把处理流 ... -
Java 多线程的一个例子
2012-10-09 09:48 978目录: 1 synchronized的 ... -
app引擎
2012-07-10 09:39 0http://sae.sina.com.cn/ htt ...
相关推荐
Java 以其“编写一次,各处运行”的特性,在短短的三年中得到了出人意料的快速发展和广泛应用,本文从多个方面对Jav a进行了介绍和评述,包括: Java语言、Java平台、Java标准、Java应用环境和Java应用等。
一个Delphi分派程序演示 一个Delphi分派程序演示
主要介绍了Java的动态分派和静态分派的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
Brighter是命令调度程序和命令处理器。 它可以与内存中的总线一起使用,或者用于微服务体系结构中的互操作性,并且可以通过范围更广的中间件... 或者,您可以编写一个事件,该事件将分派给您编写的零个或多个处理程序。
基于混合神经网络和注意力机制的软件缺陷自动分派方法.pdf
MVC架构关键技术,请求分派去重定向。精辟的内容再次,学习就是要掌握重点 难点,其他的就轻松了
在分派到@ ngrx / store之前,先对操作流进行反跳。 该库旨在与使用Angular v5 +编写的@ ngrx / store应用程序一起使用。 获取。 原料药 该库提供一种可注射NgrxActionDebouncerService与单个debounceAction方法...
能够根据员工的技能和空闲情况来分派任务。并能够对每月的员工的任务执行情况进行 统计。要用数据结构来存储数据。(用链表实现) [选作内容] 控制台菜单输出。 代码实现的功能包括: 1. 添加任务 2. 添加员工 3. ...
不过,我们前面反复说过,学习我的专栏,并不只是让你掌握知识,更重要的是锻炼你分析、解决问题的能力,锻炼你的逻辑思维能力,所以,今天我们继续把访问者模式作为引子,
论文研究-车辆调度问题的分派启发式算法.pdf, 对有时间窗的车辆调度问题进行了分析,提出了以分派为基础的启发式算法.算法中讨论了如何完成任务所需要的车辆数,定义了...
GoPipeline是一个被设计用来消息分派和处理的框架。 它用于艾润物联大数据系统的数据源采集前端,将Http WebHook、WebSocket等消息数据源, 通过GoPipeline内部转换数据格式,并分派到Spark、Kafka、WebSocketServer...
集群事件调度程序小而简单的EventDispatcher,它在所有进程中执行事件。安装 npm i --save cluster-eventdispatcher如何使用创建Eventdispatcher的实例(无论是在主服务器还是在工作服务器中): const ...
由三个类实现,写在了一个 Java 文件中:TaskDistributor 为任务分发器,Task 为待执行的任务,WorkThread 为自定的工作线程。代码中运用了命令模式,如若能配以监听器,用上观察者模式来控制 UI 显示就更绝妙不过了...
访问者模式商量篇:java的动态绑定与双分派_.docx
通过对海运集装箱空箱分派过程的仔细分析,在考虑需求不确定性的基础上引入了空箱运输能力的不确定性这一重要因素,建立了同时考虑供需平衡约束以及需求和空箱运输能力不确定性的空箱分派随机规划模型,并应用机会...
柯里昂 Java 注释处理器库,用于通过简单的语法以解耦的方式分派和连接后台任务。用法库的使用非常简单。 首先,您需要了解可用的注释。注释@Job :在后台任务类之上使用,它可以包含多个@Rule类型的@Rule 。 @Rule ...
基于机器学习和LDA主题模型的缺陷报告分派方法的Python实现。原论文为:Accurate developer r
基于Matlab解决m个人、n项任务的最优分派.pdf
网址调度一个 iOS 框架,用于将 url 请求分派到具有效果的指定 ViewController ##去做添加 URLDispatchMeta 和 URLDispatchMetaCollection 用于 url 注册。... 添加对线程安全和多线程访问的支持
GoPipeline是一个被设计用来消息分派和处理的框架。 它用于艾润物联大数据系统的数据源采集前端,将Http WebHook、WebSocket等消息数据源, 通过GoPipeline内部转换数据格式,并分派到Spark、Kafka、WebSocketServer...