为什么派生类重写方法不应该比java中的基类更严格?
为什么派生类重写方法不应该比java中的基类更具限制性。为什么编译器会抛出错误?您能解释一下原因吗?关键是,只知道您的超类的调用方应该仍然能够使用给定的子类的任何实例。考虑这种情况:为什么派生类重写方法不应该比java中的基类更严格?,java,Java,为什么派生类重写方法不应该比java中的基类更具限制性。为什么编译器会抛出错误?您能解释一下原因吗?关键是,只知道您的超类的调用方应该仍然能够使用给定的子类的任何实例。考虑这种情况: public class Super { public void print() { System.out.println("Hello!"); } } public class Sub extends Super { @Override void prin
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());
你希望它能做什么?您正在重写该方法,因此它应该打印“包访问”-但这意味着您正在从不同的包调用包访问方法
基本上,这只是一个实际的例子。您应该能够将子类的任何实例视为超类的实例,很难看出这与使子类中的内容更具限制性相适应。您不能使访问修饰符更具限制性,因为这违反了继承的基本规则,即子类实例应可替换为超类实例。 例如 假设Person类有getName公共方法,许多类(包括非子类)都在使用该方法,但有人只是将Employee添加为Person和getName的子类 Employee受保护,只能由子类访问,然后以前的代码将开始中断,Employee将不会被替换到Person对象 因此,java决定实施此限制。考虑下面的示例
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();
我们知道,对于编译器,从类哺乳动物的对象调用.readAndGet()
,但是在运行时,JVM将解析哺乳动物.readAndGet()
方法调用类人类的调用,因为哺乳动物持有新人类()
方法readAndGet
在哺乳动物
中定义为public,为了减少类人类
中的限制,我们需要删除访问说明符(使其成为默认值
),或使其受保护
或私有
现在假设
- 如果我们在
Human
中将readAndGet
定义为default
或protected
,但Human
在另一个包中定义
- 如果我们将
readAndGet
定义为人类的私有
我们知道JVM将在运行时解析readAndGet
方法调用,但编译器不知道这一点,并且在这两种情况下,代码都将成功编译,因为for compilerreadAndGet
是从类哺乳动物调用的
但在这两种情况下,JVM在运行时都无法从Human访问readAndGet
,因为在Human
类中对readAndGet
的访问受到限制
而且我们也不能确定将来谁将扩展我们的类,他将在什么包中进行扩展,如果那个家伙使覆盖方法的限制性更小,JVM可能无法调用该方法,代码将在运行时中断
因此,为了避免这种不确定性,根本不允许为子类中的重写方法分配限制性访问权限
重写方法时,我们还需要遵循其他规则,您可以阅读更多内容以了解原因。重写消息不应比重写方法更具限制性,因为子类的实例应始终能够在超类的实例所在的位置使用预期。但是,如果一个方法限制性更强,那么如果调用来自另一个类或包,则可能无法访问该方法。例如,想象一下:
//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()是private,这将抛出一个错误,因为我们在另一个类中,因此无法访问该方法 您可以引用Liskov替换原则,如果您不希望用户能够在您自己的实现中使用该函数,您可以始终使用空版本覆盖它。这样类的接口就不会中断。当然,你必须根据对给定函数的期望来决定这种方法是否有意义。@JonSkeet我很懒,不想写一个新的答案来说明这一点,所以我只是添加了一个提醒-知道你的声誉,我不应该怀疑你会添加它:-)