Java动态绑定和方法覆盖过程

时间:2017-10-20 05:53:56

标签: java object inheritance reference dynamic-binding

我知道静态绑定在编译时发生的原则,而动态绑定在运行时发生。我已经阅读了几个相关的问题。我可以按照他们中的许多思路进行思考,但是当我遇到如下具体问题时,我又搞砸了并失去了逻辑:

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)在课程taste(Cake c)中调用ChocolateCake方法?

这是我的想法: c2的静态类型为Cake,它决定要调用Cake中的方法。当涉及到运行时,动态类型的c2,即ChocolateCake,决定调用ChocolateCake cake中的方法。由于其参数的类型为ChocolateCake,因此最终会调用taste(ChocolateCake cc)

显然,这种想法是错误的。如果我认为方法的签名在编译时已经解决,因为c2的静态类型是Cake并且类Cake中只有一个方法。当涉及到运行时,它将调用类ChocolateCake中的覆盖方法。整个事情都有意义。我的困惑是,它为什么会这样工作而不是以前的方式呢?

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

ChocolateCake cc = new Cake();

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

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

任何人都可以帮助说明这个过程吗? 非常感谢!

2 个答案:

答案 0 :(得分:0)

让我尝试简化示例并完成这些步骤。为了清晰起见,我还添加了@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和调用已经选择的方法的覆盖。

答案 1 :(得分:0)

我已经尝试使用以下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. 在该目标类中的
  3. ,在这种情况下,要调用的方法取决于对象的引用类型(调用方法)和该参数的引用类型。我们从两者中选择一个更通用的一种(引用类型),并解释所调用的方法。

例如

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

只是一些自己的理解,对于Java还是很新的;)