【问题标题】:Java dynamic binding and method overriding processJava动态绑定和方法覆盖过程
【发布时间】:2017-10-20 05:53:56
【问题描述】:

我有点了解静态绑定发生在编译时而动态绑定发生在运行时的原理。我已经阅读了几个相关的问题。我可以跟随他们中的许多人的思路,但是当涉及到如下具体问题时,我又搞砸了,失去了逻辑:

class Cake {
    public void taste (Cake c) {
        System.out.println("In taste of Cake class");
    }
}

class ChocolateCake extends Cake {
    public void taste(Cake c) {
        System.out.println("In taste (Cake version) of ChocolateCake class");
    }
    public void taste(ChocolateCake cc) {
        System.out.println("In taste (ChocolateCake version) of ChocolateCake class");
    }
}

class BirthdayCake extends ChocolateCake {
    public void taste(Cake c) {
        System.out.println("In taste (Cake version) of BirthdayCake class");
    }
    public void taste (ChocolateCake cc) {
        System.out.println("In taste (ChocolateCake version) of BirthdayCake class");
    }
    public void taste(BirthdayCake bc) {
        System.out.println("In taste (BirthdayCake version) of BirthdayCake class");
    }
}

已创建以下对象:

Cake c1 = new Cake();
ChocolateCake cc = new ChocolateCake();
Cake c2 = new ChocolateCake();
Cake c3 = new BirthdayCake();

输出如下:

c1.taste(cc);//Output: In taste of Cake class
cc.taste(cc);//Output: In taste (ChocolateCake version) of ChocolateCake class
c2.taste(cc);//Output: In taste (Cake version) of ChocolateCake class
((BirthdayCake) c3).taste(cc);//Output: In taste (ChocolateCake version) of BirthdayCake class
((BirthdayCake) c3).taste((BirthdayCake) c3);//Output: In taste (BirthdayCake  version) of BirthdayCake class

基本上,我的问题是为什么c2.taste(cc)ChocolateCake 类中调用taste(Cake c)method?

这是我的想法: c2 的静态类型是 Cake,它决定了 Cake 中的一个方法将被调用。在运行时,c2的动态类型,即ChocolateCake,决定了ChocolateCake cake中的一个方法会被调用。因为它的参数类型是ChocolateCake,所以最终会调用taste(ChocolateCake cc)

显然,这种想法是错误的。如果我假设该方法的签名在编译时确定为c2 的静态类型是Cake 并且在类Cake 中只有一个方法。当涉及到运行时,它会调用类ChocolateCake 中的覆盖方法。整个事情是有道理的。我的困惑是为什么它以这种方式工作而不是以前的方式?

我不明白的另一件事是我们不允许编写如下语句,因为它会出现编译错误:

ChocolateCake cc = new Cake();

但是为什么 ChocolateCake 类型引用最终可能会传递一个 Cake 对象,因为它应该调用 ChocolateCake 类中的 taste(Cake c) 方法来获得上述正确的输出。

我想我还是不明白在对象引用上调用方法的整个过程。就像在编译时决定最佳匹配方法时发生的事情以及之后发生的事情一样,比如说,运行时(我不确定这个过程中是否还有其他阶段)。

谁能帮助说明这个过程? 非常感谢!

【问题讨论】:

  • 问题在于参数绑定不是动态的。该参数在运行时仍将是Cake,因为它在编译时绑定到Cake.cake(Cake c),但方法调用(是动态的)最终将是ChocolateCake.cake(Cake c)。这个问题可能有一个很好的副本,让我们看看我能不能找到一个。
  • 谢谢,您的回答几乎解决了我的问题。但是我仍然对我在最后几部分中提到的参数传递感到困惑。也就是说,为什么ChocolateCake 类型引用最终可以传递一个Cake 对象,而像ChocolateCake cc = new Cake(); 这样的语句甚至是不允许的?
  • 这只是基本的继承。所有蛋糕都是Cakes,但Cake 不是ChocolateCake(可能是,但这不是确定的)。你不会写Woman w = new Human();,因为那也不一定是真的。

标签: java object inheritance reference dynamic-binding


【解决方案1】:

让我尝试简化示例并完成这些步骤。为了清楚起见,我还添加了@Override。

class Cake {
    public void taste (Cake c) {
        System.out.println("In taste of Cake class");
    }
}

class ChocolateCake extends Cake {
    @Override
    public void taste(Cake c) {
        System.out.println("In taste (Cake version) of ChocolateCake class");
    }
    public void taste(ChocolateCake cc) {
        System.out.println("In taste (ChocolateCake version) of ChocolateCake class");
    }
}

ChocolateCake param = new ChocolateCake();    
Cake cake = new ChocolateCake();
cake.taste(param);

当您调用cake.taste(param); 时,java 编译器会根据引用的类型而不是引用指向的实际对象类型来选择将调用哪个方法的编译时间。

cake 的引用类型是Cake,因此编译器在基类Cake 中查找名为taste 并接受Cake 作为参数的方法。由于ChocolateCakeCake(通过继承),因此编译器会找到匹配项。

因为本质上你有一个基 taste 方法的覆盖,在运行期间并且由于动态调度,JVM 解析 cake 引用的实际类型,即 ChocolateType 并调用已经选择的方法。

【讨论】:

    【解决方案2】:

    我已尝试使用以下 main():

    public static void main(String[] args)
        {
            ChocolateCake cc = new ChocolateCake();
            Cake c = new ChocolateCake();
            Cake c1 = new Cake();
            Cake c2 = new ChocolateCake();
            Cake c3 = new BirthdayCake();
    
            ChocolateCake c4 = new BirthdayCake();
    
            c1.taste(cc);
            c1.taste(c);
    
            c2.taste(cc);
            c2.taste(c);
    
            c3.taste(cc);
            c3.taste(c);
    
            c4.taste(cc);
            c4.taste(c);
    
        }
    

    输出是这样的:

    In taste of Cake class
    In taste of Cake class
    In taste (Cake version) of ChocolateCake class
    In taste (Cake version) of ChocolateCake class
    In taste (Cake version) of BirthdayCake class
    In taste (Cake version) of BirthdayCake class
    In taste (ChocolateCake version) of BirthdayCake class
    In taste (Cake version) of BirthdayCake class
    

    到目前为止,我的理解是这样的:

    1. 寻找方法的类,取决于对象的实际类型(您可以使用动态绑定)
    2. 在该目标类中,在这种情况下,要调用哪个方法取决于对象的引用类型(调用该方法的对象)和该参数的引用类型。我们从两者中选择更通用的一个(引用类型),并解释调用的方法。

    例如

    c4.taste(cc);//c4:ChocolateCake cc:ChocolateCake -> ChocolateCake version
    c4.taste(c);//c4:ChocolateCake c:Cake -> Cake version
    

    只是自己的一些理解,对java也很陌生;)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多