通过传递子类的实例来调用超类的方法,期望子类实例上的超类参数

阿迪亚蒂瓦里

谁能解释一下以下代码的输出,这里涉及的Java原理是什么?

class Mammal {
    void eat(Mammal m) {
        System.out.println("Mammal eats food");
    }
}

class Cattle extends Mammal{
    void eat(Cattle c){
        System.out.println("Cattle eats hay");
    }
}

class Horse extends Cattle {
    void eat(Horse h) {
        System.out.println("Horse eats hay");
    }
}

public class Test {
    public static void main(String[] args) {
        Mammal h = new Horse();
        Cattle c = new Horse();
        c.eat(h);
    }
}

它产生以下输出:

Mammal eats food

我想知道我们是如何得出上述结果的。

亚历山大·伊万琴科

重载与覆盖

这不是一个有效的方法覆盖,因为所有方法签名方法名称+参数)都是不同的:

void eat(Mammal m)
void eat(Cattle c)
void eat(Horse h)

这称为方法重载请参阅),并且类Horse将具有3不同的方法,而不是一个。即它自己的重载版本eat()2继承版本。

编译器会将方法调用映射c.eat(h)到最具体的方法,即eat(Mammal m),因为变量h的类型是Mammal

为了调用带有签名的方法,eat(Horse h)您需要强制h转换为 type Horse请注意,这种转换将被视为所谓的窄化转换,它永远不会自动发生,因为无法保证这种类型转换会成功,因此编译器不会为您执行此操作。

注释掉该方法void eat(Mammal m),您将看到编译错误 - 编译器不执行缩小转换,它只能帮助您扩大转换,因为它们保证成功,因此是安全的。

如果您手动进行类型转换会发生什么:

强制h输入类型Horse

c.eat((Horse) h);

输出:

Cattle eats hay   // because `c` is of type `Cattle` method `eat(Cattle c)` gets invoked

因为变量c是类型Cattle,它只知道方法eat(Cattle c)而不是eat(Horse h)而在幕后,编译器会将类型扩大.hCattle


强制c输入h类型Horse

((Horse) c).eat((Horse) h);

输出:

Horse eats hay   // now `eat(Horse h)` is the most specific method

覆盖规则

方法覆盖的规则符合Liskov 替换原则

使用指向基类的指针或引用的函数必须能够在不知情的情况下使用派生类的对象。

子类应该以这样的方式声明它的行为,以便它可以在任何需要它的父类的地方使用:

  • 方法签名必须完全匹配即方法名称应该与参数的类型相同。并且参数应该以相同的顺序声明。

  • 重写方法的访问修饰符可以相同或更宽,但不能更严格。protected父类中的方法可以被覆盖,public也可以保留protected,我们做不到private

  • 在原始类型的情况下,被覆盖方法的返回类型应该完全相同但是如果父方法声明返回引用类型,则可以返回其子类型。即如果父返回Number一个被覆盖的方法可以Integer作为返回类型提供。

  • 如果父方法声明抛出任何已检查的异常,则允许被覆盖的方法声明相同的异常或其子类型,可以实现为安全的(或根本不抛出异常)。不允许使被覆盖的方法的安全性低于父方法声明的方法,即抛出父方法未声明的已检查异常请注意,对于运行时异常(未选中)没有任何限制,即使父方法未指定被覆盖的方法,也可以自由声明它们。

这将是方法覆盖的有效示例:

static class Mammal{
    void eat(Mammal m){
        System.out.println("Mammal eats food");
    }
}

public class Cattle extends Mammal{
    @Override
    void eat(Mammal c) {
        System.out.println("Cattle eats hay");
    }
}

public class Horse extends Cattle{
    @Override
    public void eat(Mammal h) throws RuntimeException {
        System.out.println("Horse eats hay");
    }
}

main()

public static void main(String[] args) {
    Mammal h = new Horse();
    Cattle c = new Horse();
    c.eat(h);
}

输出:

Horse eats hay

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章