2

我正在阅读一本 JAVA 书籍并遇到了 Dynamic Method Dispatch。但这对我来说有点困惑(也许是因为我是新手)。书上说它是基于一个原则:一个超类引用变量可以引用一个子类对象。

    class X{
    void display()
    {
       System.out.println("This is class X");
    }
    }

    class Y extends X{
    void display()
    {
        System.out.println("This is class Y");
    }
    void play()
    {
        System.out.println("PLAY!");
    }
    }

    class k{
    public static void main(String args[]){
    X obj1 = new X();
    Y obj2 = new Y();
    X ref  = new X();

    ref = obj1;
    ref.display();
    //output is :This is class X

    ref = obj2;   //Using the principle stated above
    ref.display();
    //output is :This is class Y

    ref.play();  //Compiler error:Play not found 
    //well it must be because ref is of X type and for X no methods of its subclass "Y"
    //is visible
    }
    }

所以我想问一下,如果 play() 不可见,那么为什么 Y 的 display() 可见?

4

4 回答 4

1

从承诺的角度来考虑。X ref 承诺它持有的东西(如果有的话)是 type X。当然,Yis 也是 type X,这就是子类化的意思:每个 ofY的实例也是Xas 的一个实例(加上一些调整和额外的特性)。但是,根据语言定义,由于内容不知道是 a ,因此play无法通过它调用该方法。Y

您可以将 a显式转换为refaY然后调用play它:

((Y) ref).play();

这是安全的,因为 Java 中的每个对象都知道自己的真实类型。如果对象引用指向错误类型的实例(例如, ),您将在运行代码时obj1得到一个。ClassCastException(如果你知道 C,这完全不同。)

于 2013-07-14T06:32:34.817 回答
0

父类引用包含从父类继承的子对象的属性。因此,您可以从包含子对象的父引用中调用所有这些方法,这些方法由子类继承和覆盖。

简而言之,引用将决定可以调用哪些方法,它所持有的对象将决定,将调用哪个方法(父/子类)。

所以我想问一下,如果 play() 不可见,那么为什么 Y 的 display() 可见?

display 是可见的,因为这是 Parent 引用的一种方法。但是游戏属于独生子女,因此父母看不到。

于 2013-07-14T06:16:58.790 回答
0

静态类型检查确保您只能调用那些属于引用的静态(声明的)类型的方法。这就是为什么您不能通过类型 X 的引用调用 .play() 的原因。

然而,动态方法分派确保,如果一个方法在子类中被覆盖,那么该特定方法将被动态调用(在运行时)。

于 2013-07-14T06:20:55.223 回答
0

在执行之前,Java 程序由编译器处理。编译器确保只调用现有的方法。在上面的例子中,编译器只知道ref是声明的X类型,并且X类型没有方法play。它不会跟踪所有的分配,也没有考虑到ref是由Y类型的obj2分配的——这种跟踪在一般情况下是不可能的,因为它可能取决于运行时数据,在编译时不可用。

另一方面,运行时的方法调用依赖于实际类型,它可以是声明类型的扩展。编译器发出的相同代码可以调用方法display的不同实现,具体取决于变量ref引用的对象的实际类型。然而,编译器会检查方法display是否存在于所有可能分配给ref的对象类型。

于 2013-07-14T08:30:05.020 回答