具有相同名称调用的非覆盖子类方法

时间:2021-06-06 14:52:22

标签: java oop inheritance polymorphism

class Animal{
    void eat(Animal animal){
        System.out.println("animal eats animal");
    }
}

public class Dog extends Animal{
    void eat(Dog dog){
        System.out.println("dog eats dog");
    }

    public static void main(String[] args) {
        Animal a = new Dog();
        Dog b = new Dog();
        a.eat(b);
        b.eat(b);
    }
}

在上面的代码中,输出将是

<块引用>

动物吃动物
狗吃狗

为什么会这样?

5 个答案:

答案 0 :(得分:5)

您可能希望看到两次“狗吃狗”。这不会发生,因为这两种方法具有不同的签名。因此,Dog#eat(Dog) 不会覆盖 Animal#eat(Animal),而是提供更具体的 eat 方法。

如果将 @Override 添加到 void eat(Dog dog) 会出现错误。使用此批注是一种很好的做法,因为它表示带批注的方法应该覆盖超类型中的方法声明。如果该方法不这样做(如您的示例中所示),您会收到以下错误,让您知道是否这样做:

<块引用>

方法不会覆盖其超类中的方法


如果要覆盖 eat 中的 Dog 方法,则需要提供相同的签名:

@Override
void eat(Animal animal) { // instead of eat(Dog dog)
    System.out.println("dog eats dog");
}

答案 1 :(得分:1)

基于继承和多态的概念

当子类具有与超类相同的签名方法时,就会发生覆盖。在您的代码中,在下面的子类 *method 中,传递的参数是一个 Dog 类型的对象,而在超类中,即 Animal,传递的参数是一个 Animal 类型的对象。

void eat(Dog dog){
        System.out.println("dog eats dog");
    }

因此您可以将上述方法更改如下以查看覆盖效果:-

void eat(Animal dog){
        System.out.println("dog eats dog");
    }

正如@Mat 所建议的,最好使用 @Override 注释,因为它会帮助 java 编译器在编译时自己找到问题。

下面我将尝试解释在更改 eat 类中的 Dog 方法的签名后继承和多态如何工作的概念:-

继承是一种将一个类建立在另一个类上的方法,就像从现有模板构建的模板一样。您可以创建一个名为“Dog”的类,作为所有 Dog 对象的模板。然后,我们可以创建另一个名为“Animal”的类,它是“Dog”类的父类。所有的狗都是动物,但并非所有的动物都是狗。我们的 Animal 类可以为所有 Animal 定义功能,然后 Dog 类可以通过扩展/继承 Animal 类来获取所有这些功能,而无需重新编写它。然后 Dog 类可以添加更多功能、更多变量和方法,这些仅适用于 Dog 对象。

Dog 类扩展了 Animal 类,这是继承。 Dog 类正在覆盖 Animal 类的eat 方法。

当我们说 Animal a = new Dog(); 时,我们声明了一个变量 a,它被声明为一个 Animal 类型,但它被初始化为一个 Dog 对象。这就是多态。因为 Dog 继承自 Animal 类,我们可以将其视为 Animal ,并将其声明为 Animal 变量类型。我们不能反过来,因为 Animal 类不是从 Dog 类继承而来的(并非所有的 Animals 都是狗)

这是因为 a 变量被视为动物数据类型。这就是为什么您将能够访问所有 Animal 类方法,但具有与 Dog 相同签名的方法将被 Dog 类的实现覆盖。请记住,等号 '=' 符号的左侧是声明,右侧是初始化。

此外,这就是为什么当您声明 Dog b = new Dog(); 并调用 eat() 方法时,它会调用类 Dog 而不是 Animal 的实现,因为它被明确提及为对象类型狗

答案 2 :(得分:0)

class Animal{
    void eat(Animal animal){
        System.out.println("animal eats animal");
    }
}

public class Dog extends Animal{
    void eat(Dog dog){
        System.out.println("dog eats dog");
    }

    public static void main(String[] args) {
        Animal a = new Dog(); //We use this when we don't know the exact runtime type of an object
        //Parent can hold any child but only parent specific methods will be called.
        Dog b = new Dog();
        a.eat(b); //Parent method will be called i.e Animal.eat(...)
        b.eat(b); //Dog Class method will be called i.e Dog.eat(...)
    }
}

答案 3 :(得分:0)

这仅仅是因为 Java 不支持 contravariant parameters。另一方面,它支持协变返回类型。 由于支持协变返回类型,子类覆盖在覆盖基类方法时可以在层次结构中具有更具体的返回类型,如下代码有效:

class Animal {
   protected Animal getAnimal() {
        System.out.println("Animal");
        return this;
   }
}
class Dog extends Animal {
  @Override 
  protected Dog getAnimal() {
     System.out.println("Dog");
     return this;
  }
}

在上面的示例中,您可以观察到 Dog.getAnimal() 返回更具体的 Dog 而不是基 Animal,但它仍然被视为覆盖,因为 Java 支持协变返回类型。< /p>

另一方面,如果你用参数来做:

class Animal {
   protected void petAnimal(Animal animal) {
        System.out.println("Petting Animal");
   }
}
class Dog extends Animal {
  @Override 
  protected void petAnimal(Dog dog) {
     System.out.println("Petting Dog");
  }
}

这不是覆盖而是过载。 因此,petAnimal() 方法(一种以 Animal 作为参数,另一种以 Dog 作为参数)被视为两种不同的方法。请记住,参数是方法签名的一部分,而返回类型不是。

第二个示例甚至不起作用,因为 @Override 注释发现该方法不是覆盖。每当您想确保覆盖时,请使用 @Override 批注,它会让您知道您是否没有覆盖该方法。 @Override 也可用于实现接口。

答案 4 :(得分:0)

首先,eat() 在子类 Dog 中没有被覆盖

通常,方法重载不一定需要继承,可以在同一个类中实现。但是,在此代码中,eat() 方法被子类 Dog 重载。

<块引用>

重载方法的区别在于传递给方法的参数的数量和类型。

因此,在编译时,它总是根据其类型选择最具体的类实现。