覆盖方法时,为什么我可以增加访问量却不能减少访问量?


72

Java为什么要指定覆盖方法的访问说明符可以比覆盖方法允许更多但不能更少的访问?例如,超类中的受保护实例方法可以在子类中公开,但不能私有。


11
使用通用描述:B extends Aif B is-a A。因此,如果A可以做action()B is-a A,那么B应该也可以做action()
戴文,2011年

Answers:


80

这是OOP中的基本原则:子类是父类的完整实例,因此必须至少提供与父类相同的接口。使受保护的/公共的东西不那么明显将违反这个想法;您可以使子类无法用作父类的实例。


嘿,我可以问您一个特定的问题吗?
Burk

@Burk我创建了一个聊天室“ patrick87-burk-private-chat”,您可以在这里与我联系以获取所需的任何信息。我试图邀请您,但是如果您没有收到邀请,您仍然应该可以找到它
Patrick87,19年

37

想象一下这两个类:

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上是私有的。因此,不可能使事情在子类上不那么显眼,因为一旦有了超类引用,您就可以访问这些东西。


3
这就是Liskov替换原理的含义。
RainDoctor 2015年

9

因为这很奇怪:

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!

9

举一个下面给出的例子

 class Person{
 public void display(){
      //some operation
    }
 }

class Employee extends Person{
   private void display(){
       //some operation
   }
 }

在以下情况下会发生典型的覆盖

Person p=new Employee();

p是我们调用p.display()时类型为Person(super class)的对象引用 。由于访问修饰符的限制更严格,因此对象引用p 无法访问Employee类型的子对象


4

迟到了,但我想添加一个与覆盖有关的更多问题:覆盖方法必须比覆盖方法允许更少(或相同级别的)可抛出异常;甚至什么都没有抛出。

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来打破这一点(否则必须使用救护车)

我经常将最重要的规则称为“ [可以]在[级别]上有更多的访问权限,但在[例外]上有较少的例外”


0

因为子类是超类的特化,换句话说,它是超类的扩展。

想象一下例如toString方法。所有Java对象都具有它,因为Object类具有它。假设您可以使用toString private方法定义一个类。然后,您将不再平等地对待所有对象。例如,您将不再能够安全地执行此操作:

for (Object obj : collection) System.out.println(obj);


0

那么,就您提到的特定情况而言,Java将如何处理呢?如果子类将公共/受保护的方法设为私有,那么在子类的实例上调用该方法时,JVM应该做什么?尊重私有并调用超类的实现?此外,当您突然说“尽管合同最初说了什么,但是没有人可以使用此方法”时,您正在违反超类指定的合同。


0

要改写已经说过的话,这与Java如何编译成字节码然后由JVM解释有关。当子类覆盖其父方法之一时,编译器将使用引用类型来确定要使用两种方法中的哪一种。然后,JVM在运行时使用对象类型来确定应真正使用的方法。

在上面的示例中;Animal lion = new Lion(), 什么时候lion.getName()被调用时,编译器使用该方法的Animal版本,并且JVM替换它/可以将其替换为Lion版本,因为它完全“适合”。但是,如果允许Lion对getName()的限制比对Animal的getName()的限制要大,那么您可以绕开该限制,因为如果Lion对象具有Animal引用,则编译器会将其视为不受限制。

为了解决这个问题,java规定孩子对覆盖的方法进行比对其覆盖的方法更严格的限制是非法的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.