为什么派生类重写方法不应该比java中的基类更具限制性。为什么编译器会抛出错误?你能解释一下原因吗?
答案 0 :(得分:28)
重点是,只知道超类的调用者仍然可以使用它给出的子类的任何实例。考虑一下这种情况:
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 Substitution Principle的一个例子。您应该能够将子类的任何实例视为超类的实例,并且很难看出它如何适应在子类中使事情更具限制性。
答案 1 :(得分:6)
您不能使访问修饰符更具限制性,因为这会违反继承的基本规则,即子类实例应该可以替换超类实例。
例如 假设 Person 类具有 getName公共方法,许多类(包括非子类)正在使用它。但是有人刚刚添加了员工 Person的子类和 getName in 员工受保护,只能通过子类访问,然后先前的代码将开始破坏,而Employee将无法替换为Person对象。
因此,java已经决定强制进行此次重组。
答案 2 :(得分:1)
考虑以下示例
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()
方法调用来自类{{1}的调用因为Human
持有mammal
。
方法new Human()
已在readAndGet
中公开定义,为了减少课程Mammal
中的限制,我们需要删除访问说明符(使其成为Human
或者将其设为default
或protected
。
现在假设
private
中将readAndGet
定义为default
或protected
,但在另一个包中定义Human
Human
定义为私有
我们知道JVM会在运行时解析readAndGet
方法调用,但编译器并不知道这一点,在这两种情况下,代码都会成功编译,因为编译器readAndGet
是从类{{1}调用的}}
但是在运行时的两种情况下,JVM都无法访问Human的readAndGet
,因为Mammal
类中readAndGet
的限制性访问会限制它。
而且我们也不能确定将来会扩展我们的类以及他将要做什么包,如果那个人使覆盖方法更少限制JVM可能无法调用方法和代码将在运行时中断。
因此,为了避免这种不确定性,根本不允许在子类中分配对覆盖方法的限制性访问。
在覆盖方法时我们还需要遵循其他规则,您可以在Why We Should Follow Method Overriding Rules上阅读更多内容以了解原因。
答案 3 :(得分:1)
重载消息不应比重载方法更具限制性,因为子类的实例应始终能够在期望超类的实例的地方使用。但是,如果方法的限制性更强,则如果调用来自另一个类或包,则可能无法访问该方法。例如,想象一下:
//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()是私有的,则将抛出错误,因为我们在不同的类中,因此无法访问该方法。