Java 方法调用之动态单分派与静态单多分派

定义:根据一个宗量的类型进行方法的选择称为单分派;

根据多于一个宗量的类型对方法的选择称为多分派;

方法的接受者与方法的参数统称为方法的宗量

 

public class Dispatcher {
    static class QQ {}
    static class _360 {}

    public static class Father {
        public void hardChoice(QQ arg) {
            System.out.println("father choose QQ");
        }

        public void hardChoice(_360 arg) {
            System.out.println("father choose _360");
        }
    }

    public static class Son extends Father {
        @Override
        public void hardChoice(QQ arg) {
            System.out.println("son choose QQ");
        }

        @Override
        public void hardChoice(_360 arg) {
            System.out.println("son choose 360");
        }
    }

    public static void main(String[] args) {
        Father father = new Father();
        Father son = new Son();
        father.hardChoice(new _360());
        son.hardChoice(new QQ());
    }
}

执行javap:
javap -verbose -c Dispatcher

执行结果如下所示:

Classfile /work/test/Dispatcher.class
  Last modified Jan 17, 2018; size 581 bytes
  MD5 checksum 09a577366cd2c3909d09c89558423dff
  Compiled from "Dispatcher.java"
public class Dispatcher
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #13.#27        // java/lang/Object."<init>":()V
   #2 = Class              #28            // Dispatcher$Father
   #3 = Methodref          #2.#27         // Dispatcher$Father."<init>":()V
   #4 = Class              #29            // Dispatcher$Son
   #5 = Methodref          #4.#27         // Dispatcher$Son."<init>":()V
   #6 = Class              #30            // Dispatcher$_360
   #7 = Methodref          #6.#27         // Dispatcher$_360."<init>":()V
   #8 = Methodref          #2.#31         // Dispatcher$Father.hardChoice:(LDispatcher$_360;)V
   #9 = Class              #32            // Dispatcher$QQ
  #10 = Methodref          #9.#27         // Dispatcher$QQ."<init>":()V
  #11 = Methodref          #2.#33         // Dispatcher$Father.hardChoice:(LDispatcher$QQ;)V
  #12 = Class              #34            // Dispatcher
  #13 = Class              #35            // java/lang/Object
  #14 = Utf8               Son
  #15 = Utf8               InnerClasses
  #16 = Utf8               Father
  #17 = Utf8               _360
  #18 = Utf8               QQ
  #19 = Utf8               <init>
  #20 = Utf8               ()V
  #21 = Utf8               Code
  #22 = Utf8               LineNumberTable
  #23 = Utf8               main
  #24 = Utf8               ([Ljava/lang/String;)V
  #25 = Utf8               SourceFile
  #26 = Utf8               Dispatcher.java
  #27 = NameAndType        #19:#20        // "<init>":()V
  #28 = Utf8               Dispatcher$Father
  #29 = Utf8               Dispatcher$Son
  #30 = Utf8               Dispatcher$_360
  #31 = NameAndType        #36:#37        // hardChoice:(LDispatcher$_360;)V
  #32 = Utf8               Dispatcher$QQ
  #33 = NameAndType        #36:#38        // hardChoice:(LDispatcher$QQ;)V
  #34 = Utf8               Dispatcher
  #35 = Utf8               java/lang/Object
  #36 = Utf8               hardChoice
  #37 = Utf8               (LDispatcher$_360;)V
  #38 = Utf8               (LDispatcher$QQ;)V
{
  public Dispatcher();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: new           #2                  // class Dispatcher$Father
         3: dup
         4: invokespecial #3                  // Method Dispatcher$Father."<init>":()V
         7: astore_1
         8: new           #4                  // class Dispatcher$Son
        11: dup
        12: invokespecial #5                  // Method Dispatcher$Son."<init>":()V
        15: astore_2
        16: aload_1
        17: new           #6                  // class Dispatcher$_360
        20: dup
        21: invokespecial #7                  // Method Dispatcher$_360."<init>":()V
        24: invokevirtual #8                  // Method Dispatcher$Father.hardChoice:(LDispatcher$_360;)V
        27: aload_2
        28: new           #9                  // class Dispatcher$QQ
        31: dup
        32: invokespecial #10                 // Method Dispatcher$QQ."<init>":()V
        35: invokevirtual #11                 // Method Dispatcher$Father.hardChoice:(LDispatcher$QQ;)V
        38: return
      LineNumberTable:
        line 37: 0
        line 38: 8
        line 39: 16
        line 40: 27
        line 41: 38
}
SourceFile: "Dispatcher.java"
InnerClasses:
     public static #14= #4 of #12; //Son=class Dispatcher$Son of class Dispatcher
     public static #16= #2 of #12; //Father=class Dispatcher$Father of class Dispatcher
     static #17= #6 of #12; //_360=class Dispatcher$_360 of class Dispatcher
     static #18= #9 of #12; //QQ=class Dispatcher$QQ of class Dispatcher

 

从上面的字节码指令中可以看到,两次方法调用

father.hardChoice(new _360());
son.hardChoice(new QQ());

对应的字节码指令都是一样的,只是参数不同而已:

invokevirtual #8; //Method Dispatcher$Father.hardChoice:(LDispatcher$_360;)V
invokevirtual #11; //Method Dispatcher$Father.hardChoice:(LDispatcher$QQ;)V

 

由此可见,在class文件中都是调用Father的hardChoice()方法。

解析

在java源代码进行编译的过程中,发生了这么个事情。
首先确定方法的接收者,发现两个对象变量的静态类型都是Father类型的,因此在class文件中写的Father类中方法的符号引用。
再者,对于方法参数,一个是_360对象,一个是QQ对象,按照静态类型匹配的原则,自然找到各自的方法。
上面的两步都是在编译器中做出的,属于静态分派,在选择目标方法时根据了两个宗量,是多分派的。因此,静态分派属于多分派类型。
当java执行时,当执行到son.hardChoice(new QQ()); 时,发现son的实际类型是Son,因此会调用Son类中的方法。在执行father.hardChoice(new _360()); 时也有这个过程,只不过father的实际类型就是Father而已。发现,在目标选择时只依据了一个宗量,是单分派的。因此,动态分派属于单分派类型。

结论

到目前为止,java语言是一个静态多分派,动态单分派的语言。下篇博文,将讨论讨论动态分派(即多态)的实现原理

Jason.wang

When you find your talent can't afford to be ambitious, be quiet and study !

You may also like...