为什么派生类重写方法不应该比java中的基类更具限制性?

时间:2013-04-24 06:24:57

标签: java

为什么派生类重写方法不应该比java中的基类更具限制性。为什么编译器会抛出错误?你能解释一下原因吗?

4 个答案:

答案 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或者将其设为defaultprotected

现在假设

  • 如果我们在private中将readAndGet定义为defaultprotected,但在另一个包中定义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()是私有的,则将抛出错误,因为我们在不同的类中,因此无法访问该方法。