gpt4 book ai didi

java - 引用的类类型和实际的类类型,哪个决定调用哪个方法?

转载 作者:搜寻专家 更新时间:2023-10-31 20:00:14 26 4
gpt4 key购买 nike

这个问题在这里已经有了答案:





Polymorphism in Overloaded and Overridden Methods

(4 个回答)


4年前关闭。




标题可能具有误导性,但作为非本地人,我想不出更好的标题。

假设我有两个类,DogFox :

public class Dog {
public String bark() {
return "Wuff";
}
public String play(Dog d) {
return "Wuff" + d.bark();
}
}


public class Fox extends Dog {
public String bark() {
return "Ringding" ;
}
public String play(Fox f) {
return "Ringding" + f.bark();
}
}

我创建了一些实例并调用了一些方法
Fox foxi = new Fox();
Dog hybrid = new Fox();
System.out.println(hybrid.play(foxi)); // Output number 1
System.out.println(foxi.play(hybrid)); // Output number 2

对于 1. 我期望的输出 "RingdingRingding"因为 hybrid实际上是对 Dog 实例的引用,即使引用的类型为 Dog它仍然指的是 Fox对象,但我仍然得到这个输出:

WuffRingding



第二个我遇到了同样的问题,因为 foxiFox 的一个实例和 hybrid实际上是 Fox 的一个实例(不管是什么引用,对吧?),输出应该是 "RingdingRingding"但话又说回来,我得到了:

WuffRingding



有人可以解释为什么吗?

最佳答案

方法调用的两个重要事项。

你有两次:编译时间和运行时间。
这两次之间的规则也不相同。

  • 在编译时,编译器必须静态确定调用哪个方法的确切签名才能正常编译。
    此绑定(bind)是静态的,因为编译器与调用方法的具体实例无关,传递给方法的参数也是一样的。
    编译器不依赖于有效类型,因为在运行时有效类型可能会在执行流程中发生变化。
    所以编译器在可用的方法中搜索一个声明的类型,这是根据传递给它的参数的声明类型更具体的方法。
  • 在运行时,将根据调用该方法的有效实例使用来自一个类或另一个类的实例方法。
    但是被调用的方法必须尊重编译时指定的签名。

  • 1)对于第一种情况:
    Fox foxi = new Fox();
    Dog hybrid = new Fox();
    System.out.println(hybrid.play(foxi)); // Output number 1
  • 第一次(编译时间):

  • 对于 Dog 实例,编译器必须找到最具体的方法 play()使用 Fox 声明类型的变量作为参数。

    在 Dog 类中,单个方法 play()具有兼容的签名存在:
    public String play(Dog d) {

    所以这个签名用于绑定(bind): String play(Dog d) .

    关于 bark()方法,很明显,因为只有一个 bark() 方法签名。
    所以我们对编译时绑定(bind)的方法没有歧义
  • 第二次(运行时):

  • 在运行时 String play(Dog d)调用具体实例的方法。 hybrid变量引用 Fox 的一个实例,但 Fox 没有覆盖 String play(Dog d) . Fox 定义了一个 play() 方法,但带有另一个签名:
    public String play(Fox f) {

    所以JVM调用 public String play(Dog d) {狗的方法。
    然后调用有效类型 d的方法当 d.bark()被执行并且 dFox实例。

    因此输出“WuffRingding”。

    2)对于第二种情况:
    Fox foxi = new Fox();
    Dog hybrid = new Fox();
    System.out.println(foxi.play(hybrid)); // Output number 2
  • 第一次(编译时间):

  • 对于 Fox例如,编译器必须找到最具体的方法 play()Dog 为参数的变量声明的类型。

    Fox两类 play()存在具有兼容参数的方法:
    public String play(Dog d) { // inherited from the parent class

    public String play(Fox f) { // declared in Fox

    编译器必须为方法的调用上下文选择更具体的方法
    它标识了一个比另一个更具体的方法 Dog声明类型参数: public String play(Dog d) .
    所以编译器绑定(bind)了 play()public String play(Dog d) 的方法调用当它编译类时。
  • 第二次(运行时):

  • 在运行时 String play(Dog d)调用具体实例的方法。
    至于第一种情况, foxi变量引用 Fox 的一个实例,但 Fox 没有覆盖 String play(Dog d) .
    所以JVM调用 public String play(Dog d)狗的方法。
    然后调用有效类型 f的方法当 f.bark()被执行并且 fFox实例。

    因此再次输出“WuffRingding”。

    为了避免这种意外,您应该添加 @Override在旨在覆盖父类方法的方法中:
    例如 :
    @Override
    public String play(Fox f) {
    return "Ringding" + f.bark();
    }

    如果方法没有有效地覆盖 play(Fox f)层次结构中的方法,编译器会提示它。

    关于java - 引用的类类型和实际的类类型,哪个决定调用哪个方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42226737/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com