Java中指向子类的父类对象调用子类重载的方法,传入指向子类对象的父类引用,为什么最终还是调用了父类的方法?

Dog类实现了Animal接口,Animal a = new Dog(); 创建子类对象,a指向的应该是一个Dog类的对象,a.bark(d); 调用的应该是Dog重载的bark方法才对,可为什么最终调用的还是接口Animal的方法呢。

public interface Animal {
    default void bark(Animal a) {
        System.out.println("Animal");
    }
}

public class Dog implements Animal {
    // Overload
    public void bark(Dog d) {
        System.out.println("Dog");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal a = new Dog();
        Dog d = new Dog();
        a.bark(a); // Animal
        a.bark(d); // Animal

        d.bark(a); // Animal
        d.bark(d); // Dog
    }
}

6个回答

  1. a是一个Animal类型引用,调用方法时,编译器会先去Animal类中的虚函数表中找有没有可以调用的方法。编译器找到Animal中的bark方法,而bark并没有被Dog实现,且Animal中的bark方法参数类型为Animal,而Dog为Animal的实现类,所以函数可以使用,于是编译器偷了个懒就不去Animal类的虚函数表中去匹配方法了,所以a.bark(d),调用的是接口中的方法。

  2. 而d是一个Dog类型引用。编译器会先去Dog类的虚函数表中找有没有可以调用的方法,找到Dog中的bark方法和Animal中的bark方法,因为参数d为Dog类型,固根据参数匹配原则选择Dog类中的bark方法。

  3. 总的来说,接口类型的引用,优先使用接口中没被实现且可以用的方法。在Animal类的虚函数表中,只有Animal的bark方法地址,没有Dog中的bark方法地址。

  4. 深究可以去看看虚函数表,不过这个概念只在C++中存在,而java和C++本是同根生。

  5. 原创手打,忘采纳。

Java语言不支持重写方法的协变(Contravariant)
也就是public void bark(Dog d)只能定义为
public void bark(Animal d)
你非要那么写,则两个bark是不同的方法,不构成覆盖,只能是重载而已。

楼上回答的很正确,同时补充一下如果你要是想a也调用子类的方法应该再次使用向下转型Dog a1 = (Dog)a;这个时候调用的是子类方法

可能是因为这俩不是一个方法吧 ,他们的方法参数不一样 , 我感觉你把Override注释掉是不是因为编辑器给你提示报错说这里不能使用Override , 因为他们不是同一个方法,你把方法的参数都去掉就行了, 反正这个方法的参数你也没有用到

因为方法重写没有成功,输入参数可以放大但不能缩小,你把public void bark(Dog d) 中的Dog改成Animal就可以了

Animal a = new Dog(); 是dog实现了Animal 类的接口 调用的自然是 Animal的, 为什么bark()可以传递Dog 应该是dog是 Animal的实现类(Animal a = new Dog(); )。,没有更好的解释,如果有大神可以通知我一下。

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问