重载与重写
这不是有效的method overriding,因为所有method signatures(method name + parameters)都不同:
void eat(Mammal m)
void eat(Cattle c)
void eat(Horse h)
这称为105(see),类Horse
将有3
个不同的方法,而不是一个.一、 e.eat()
和2
继承版本的overloaded版本.
编译器将方法调用c.eat(h)
映射到最具体的方法eat(Mammal m)
,因为变量h
的类型是Mammal
.
为了使用签名eat(Horse h)
调用该方法,需要将h
强制为类型Horse
.请注意,这种转换将被视为所谓的narrowing conversion,而且它永远不会自动发生,因为不能保证这种类型转换会成功,所以编译器不会为您执行转换.
注释掉方法void eat(Mammal m)
,您将看到编译错误-编译器不执行narrowing conversions,它只能帮助您执行widening conversions,因为它们保证会成功,因此是安全的.
如果手动进行类型转换,会发生什么情况:
将h
强制为Horse
型:
c.eat((Horse) h);
Output:
Cattle eats hay // because `c` is of type `Cattle` method `eat(Cattle c)` gets invoked
因为变量c
的类型是Cattle
,所以它只知道方法eat(Cattle c)
,而不知道方法eat(Horse h)
.在幕后,编译器会将h
转换为Cattle
类型.
将c
和h
强制为Horse
型:
((Horse) c).eat((Horse) h);
Output:
Horse eats hay // now `eat(Horse h)` is the most specific method
覆盖规则
method overriding的规则符合Liskov substitution principle.
使用指向基类的指针或引用的函数必须能够在不知情的情况下使用派生类的对象.
子类应该以这样的方式声明其行为,以便可以在需要父类的任何地方使用它:
Method signatures必须与exactly匹配.一、 e.方法名称应与参数类型相同.和参数应按相同的顺序声明.
重写方法的access modifier可以相同或更宽,但不能更严格.一、 父类中的protected
方法可以重写为public
,也可以保留为protected
,但我们不能将其改为private
.
在 case primitive type中,重写方法的Return type应该完全相同.但如果父方法声明返回引用类型,则可以返回其子类型.一、 e.如果父级返回Number
,则重写的方法可以提供Integer
作为返回类型.
如果父方法声明抛出任何checked exceptions,则允许重写的方法声明相同的exceptions或其子类型,可以实现为安全的(或根本不抛出异常).不允许使重写方法的安全性低于父方法声明的方法,即抛出checked exceptions个未由父方法声明的方法.注意,runtime exceptions没有限制(未选中),被重写的方法可以自由声明它们,即使它们不是由父方法指定的.
这是method overriding的一个有效示例:
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);
}
Output:
Horse eats hay