为什么Java指定覆盖方法的访问说明符可以比覆盖方法允许更多但不能更少的访问?例如,超类中的受保护实例方法可以在子类中公开,但不能私有。
这是OOP中的基本原理:子类是父类的完整实例,因此必须与父类呈现至少相同接口。使受保护的/公共的东西不那么明显将违反此想法;您可以使子类无法用作父类的实例。
想象这两个类:
public class Animal {
public String getName() { return this.name; }
}
public class Lion extends Animal {
private String getName() { return this.name; }
}
我可以编写这段代码:
Animal lion = new Lion();
System.out.println( lion.getName() );
而且这必须是有效的,因为在Animal上方法getName()是公开的,即使方法在Lion上是私有的。因此,不可能使事情在子类上不那么显眼,因为一旦有了超类引用,您就可以访问此东西。
下面举一个例子
class Person{
public void display(){
//some operation
}
}
class Employee extends Person{
private void display(){
//some operation
}
}
在以下情况下会发生典型的覆盖
Person p=new Employee();
这里p
是我们调用时类型为Person(super class)的对象引用p.display()。由于访问修饰符的限制更为严格,因此对象引用p
无法访问类型为Employee的子对象
因为这很奇怪:
class A {
public void blah() {}
}
class B extends A {
private void blah() {}
}
B b = new B();
A a = b;
b.blah(); // Can't do it!
a.blah(); // Can do it, even though it's the same object!
晚聚会,但我想补充一点与覆盖有关的问题:覆盖方法必须比覆盖方法允许更少(或相同级别的)可抛出异常;甚至根本没有抛出。
Liskov替换原理也可以解释:
interface Actionable {
void action() throws DislocationException;
}
public class Actor implements Actionable {
@Override
public void action() throws DislocationException {
//....
}
}
public class Stuntman implements Actionable {
@Override // this will cause compiler error
public void action() throws DislocationException, DeathException {
//....
}
}
// legacy code to use Actionable
try {
Actionable actor = new Actor(); // this cannot be replaced by a Stuntman,
// or it may break the try/catch block
actor.action();
} catch (DislocationException exc) {
// Do something else
}
[上面,重写的方法做出了一个承诺,即在最坏的情况下,它将不再引发DislocationException(在拍摄位置需要医生)。因此,通过添加更多DeathException(或必须使用救护车),覆盖方法一定不能破坏这一点。
我经常将覆盖规则称为“ [可以]在[级别]上有更多的访问权限,但[但]有更少的例外”
因为子类是超类的特化,换句话说,它是超类的扩展。
例如,设想toString方法。所有Java对象都具有它,因为Object类具有它。假设您可以使用toString private方法定义一个类。然后,您将不再平等地对待所有对象。例如,您将不再能够安全地进行此操作:
for (Object obj : collection) System.out.println(obj);
好吧,就您提到的特定情况而言,Java将如何处理呢?如果子类将公共/受保护的方法设为私有,那么在子类的实例上调用该方法时,JVM应该做什么?尊重私有并调用超类的实现?此外,当您突然说“尽管合同最初说了什么,但是没有人可以访问此方法”时,您正在违反超类指定的合同。
要重新表达已经说过的话,这与如何将Java编译成字节码然后由JVM解释有关。当子类覆盖其父方法之一时,编译器将使用引用类型来确定要使用两种方法中的哪一种。然后,JVM在运行时使用对象类型来确定应真正使用的方法。