Why derived class overriding method should not be more restrictive than base class in java.Why compiler throws error?can you please anyone explain reason for that?
4 回答
关键是只知道你的超类的调用者应该仍然能够使用它给定的子类的任何实例。考虑这种情况:
public class Super
{
public void print()
{
System.out.println("Hello!");
}
}
public class Sub extends Super
{
@Override
void print() // Invalid
{
System.out.println("Package access");
}
}
现在从一个不同的包,想象我们有:
public void printSuper(Super x)
{
x.print();
}
我们称之为:
printSuper(new Sub());
你希望它做什么?您正在覆盖该方法,因此它应该打印“包访问”-但这意味着您正在从不同的包调用包访问方法...
基本上,这只是Liskov 替换原则的一个例子。您应该能够将子类的任何实例视为超类的实例,并且很难看出这如何与使子类中的事物更具限制性相适应。
您不能使访问修饰符更具限制性,因为这将违反继承的基本规则,即子类实例应该可以替换超类实例。
例如,假设Person类具有getName 公共方法,许多类(包括非子类)正在使用该方法。但是有人刚刚将Employee添加为 Person 的子类,并且Employee 中的 getName 受到保护,只能由子类访问之前的代码将开始中断,并且 Employee 将无法替换为 Person 对象。
因此java决定强加这个限制。
覆盖消息不应比覆盖方法更具限制性,因为子类的实例应该始终能够在需要超类实例的地方使用。但是,如果一个方法的限制性更强,如果调用来自另一个类或包,则它可能无法访问。例如,想象一下:
//File Number 1
public class Human {
public int age = 21;
public int getAge() {
System.out.println("getAge() method in Human");
return age;
}
}
//File Number 2
public class Teacher extends Human {
public int getAge() {
System.out.println("getAge() method in Teacher");
return age;
}
}
//File Number 3
public class Characteristics {
public static void showAgeOfObject(Human human) {
human.getAge();
}
public static void main(String[] args) {
Human human = new Human();
Teacher teacher = new Teacher();
showAgeOfObject(human);
showAgeOfObject(teacher);
}
}
现在如果两个 getAge() 方法都是公开的,这将被显示
getAge() method in Human
getAge() method in Teacher
但是如果 Teacher 中的 getAge() 是私有的,这将引发错误,因为我们在不同的类中,因此无法访问该方法。
考虑下面的例子
class Mammal {
public Mammal readAndGet() throws IOException {//read file and return Mammal`s object}
}
class Human extends Mammal {
@Override
public Human readAndGet() throws FileNotFoundException {//read file and return Human object}
}
如果我们执行下面的代码
Mammal mammal = new Human();
Mammal obj = mammal.readAndGet();
而且我们知道编译器mammal.readAndGet()
是从类的对象中调用的,Mammal
但是在运行时 JVM 会将mammal.readAndGet()
方法调用解析为类的调用,Human
因为mammal
它正在持有new Human()
.
方法readAndGet
是在 public 中定义的Mammal
,为了减少它在类中的限制,Human
我们需要删除访问说明符(使其成为default
)或使其成为protected
or private
。
现在假设
- 如果我们定义
readAndGet
为default
或protected
在Human
但Human
在另一个包中定义 - 如果我们
readAndGet
在 Human 中定义为私有
我们知道 JVM 会在运行时解析readAndGet
方法调用,但编译器不知道这一点,在这两种情况下,代码都会成功编译,因为 for compilerreadAndGet
是从 class 调用的Mammal
。
但是在这两种情况下,在运行时 JVM 都将无法从 Human 访问,因为它会由于类中readAndGet
的限制性访问而受到限制。readAndGet
Human
而且我们也不能确定谁将在未来扩展我们的类,他将在哪个包中这样做,如果那个人使覆盖方法的限制更少,JVM 可能无法调用该方法并且代码将在运行时中断.
因此,为了避免这种不确定性,根本不允许对子类中的覆盖方法分配限制性访问。
在覆盖方法时,我们还需要遵循其他规则,您可以阅读更多关于为什么我们应该遵循方法覆盖规则以了解原因。