Java 单分派 多分派 解析

今天在看秦小波的《设计模式之禅》的Visitor模式的时候,书中有提到:单分派(single dispatch)和双分派(double dispatch,多分派(multi-dispatch)的一种),以及动态绑定和静态绑定和重载(overload)、覆盖(override)之关联,在网上找了下资料:

 

发现以下几个文章解释的比较好:(其中错误的地方有改动)

一、【转自】http://hi.baidu.com/haoyan665/blog/item/239c0be775aebe2ab9382090.html

 

变量被声明时的类型叫做变量的静态类型(Static Type) 又叫明显类型(Apparent Type)

变量所引用的对象的真实类型又叫做变量的实际类型(Actual Type)

如:

Horse wh = new WhiteHorse()

Horse 为静态类型、WhiteHorse 为实际类型。

 

根据对象的类型而对方法进行的选择,就是分派(Dispatch)

分派分静态分派和动态分派。

 

静态分派(Static Dispatch) 发生在编译时期,分派根据静态类型信息发生。

方法重载(Overload)就是静态分派。

动态分派(Dynamic Dispatch) 发生在运行时期,动态分派动态地置换掉某个方法。面向对象

              的语言利用动态分派来实现方法置换产生的多态性。

 

静态分派实例:

 

public class Mozi{    

       public void ride(Horse h){

              System.out.println("Riding a horse");

       }

 

       public void ride(WhiteHorse h){

              System.out.println("Riding a white horse");

       }

 

       public void ride(BlackHorse h){

              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);

       }

}

 

       运行时程序打印出相同的两行“Riding a horse”。也就是都调用了方法ride(Horse h)。两次对ride()方法的调用传入的是不同的参量,也就是whbh。它们虽然具有不同的真实类型,但是它们的静态类型都是一样的,均是Horse 类型。

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

动态分派实例:

       Java通过方法的置换(Overriding)支持动态分派。

    String s1 = "ab";

              Object o = s1 +"c";

              String s = "abc";

              boolean b = o.equals(s);

       变量s1 s的静态类型和真实类型都是String,而o的静态类型是Object,真实类型则是Object类型的一个子类型(String)

       如果上面最后一行的equals()方法调用的是String类的equals()方法,那么上面代码检查的就是o的值是否等于字符串“abc”;相反,如果上面的equals()方法调用的是Object类的equals()方法,那么松检查的就是o所指的对象和s1所指的对象是不是同一个对象。

       问题的核心是Java编译器在编译时期并不总是知道哪一些代码会被执行。因为种种原因编译器仅仅知道对象的静态类型,而不知道对象的真实类型;而方法的调用则是根据对象的真实类型,而不是静态类型。仍以上面的代码为例。变量o指向一个类型为String的对象。这个String对象的值是“abc”。所上面最后一行的equals方法调用的是String类的equals()方法。所上面的最后的b值为true

 

分派类型

一个方法所属的对象叫做方法的接收者,方法的接收者与方法的参量统称做方法的宗量。

根据分派可以基于多少种宗量,可以将面向对象的语言划分为单分派语言和多分派语言.单元分派语言根据一个宗量的类型进行对方法的选择,多分派语言根据多于一个的宗量的类型对方法进行选择。

       在一个支持动态单分派的语言里面,有两个条件决定了一个请求会调用哪一个操作:一是请求的名字,二是接收者的真实类型。单分派限制了方法的选择过程,使得只有一个宗量可以被考虑到,这个宗量通常就是方法的接收者。在JAVA语言里面,如果一个操作是作用于某个类型不明的对象上面的。那么这个对象的真实类型测试仅会发生一次。这个就是动态的单分派的特征。

       一言以蔽之,JAVA语言支持静态的多分派和动态的单分派。

实例:

 

public class Point{

       int x,y;

       void draw(){

       }

       void translate(int one){

       }

 

       void translate(int one,int two){

       }

}

 

public class ColorPoint extends Point{

       Color c;

       void draw(){

       }

}

 

public class Client{    

       private static Point p;

       private static Point pc;

 

       public static void main(String[] args) {

              p = new Point();

              pc = new ColorPoint();

              //静态多分派

              p.translate(5);

              p.translate(1,2);

              //动态单分派

              p.draw();//调用Point.draw();

              pc.draw();//调用ColorPoint.draw();

       }

}

 

二、【转自】http://dreamstone.iteye.com/blog/77918

 

一,静态分派:
1,定义:发生在编译时期,分派根据静态类型信息发生,重载就是静态分派
2,什么是静态类型:变量被声明时的类型是静态类型
      什么是动态类型:变量所引用的对象的真实类型
3,有两个类,BlackCat ,WhiteCat都继承自Cat
如下调用

class  Cat {}
class  WhiteCat  extends  Cat {}
class  BlackCat  extends  Cat {}
public   class  Person  {
    
public void feed(Cat cat){
        System.out.println(
"feed cat");
    }

    
public void feed(WhiteCat cat){
        System.out.println(
"feed WhiteCat");
    }

    
public void feed(BlackCat cat){
        System.out.println(
"feed BlackCat");
    }

    
public static void main(String[] args) {
        Cat wc 
= new WhiteCat();
        Cat bc 
= new BlackCat();
        Person p 
= new Person();
        p.feed(wc);
        p.feed(bc);
    }


}

运行结果是:
feed cat
feed cat
这样的结果是因为重载是静态分派,在编译器执行的,取决于变量的声明类型,因为wc ,bc都是Cat所以调用的都是feed(Cat cat)的函数.
二,动态分派
1,定义:发生在运行期,动态分派,动态的置换掉某个方法。
还是上边类似的例子:

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

}

public   class  BlackCat  extends  Cat {
    
public void eat(){
        System.out.println(
"black cat eat");
    }

    
public static void main(String[] args){
        Cat cat 
= new BlackCat();
        cat.eat();
    }

}

这个时候的结果是:
black cat eat
这样的结果是因为在执行期发生了向下转型,就是动态分派了。

三,单分派:
1,定义:根据一个宗量的类型进行方法的选择

四,多分派:
1,定义:根据多于一个宗量的类型对方法的选择
2,说明:多分派其实是一系列的单分派组成的,区别的地方就是这些但分派不能分割。
3,C++ ,Java都是动态单分派,静态多分派语言
多分派的语言有:CLOS  Cecil

最后的部分:

看完本文,如果你对visitor模式有更多的兴趣,想了解更多请看如下几篇文章。
1,静态分派,动态分派,多分派,单分派 --------------   visitor模式准备(本文)
2,访问差异类型的集合类 ------------------------   visitor模式入门
3,visitor模式理论及学术概念-------------------   visitor模式进一步
4,重载overloading和覆写overriding哪个更早执行--   visitor帮助篇
虽然排列顺序是1,2,3,4但是我个人建议的学习方式是2,1,3,4因为这个顺序更方便一般人理解

 

你可能感兴趣的:(java,设计模式,C++,c,单元测试)