为什么我们不能在子类中分配较弱的权限

时间:2013-07-07 07:36:10

标签: java inheritance

我有一个类,它有一个方法,默认情况下访问说明符是public。现在,我想在子类中扩展此类,并且我想覆盖此方法以使访问说明符为“private”。编译此代码时,我收到编译错误:

  

“尝试分配较弱的访问权限”。

有人可以向我解释在子类中分配较弱的权限有什么问题吗?

以下是导致编译错误的代码:

class Superclass 
{
    void foo() 
    {
        System.out.println("Superclass.foo");
    }
}

class Subclass extends Superclass 
{
    private void foo() 
    {
        System.out.println("Subclass.foo");
    }
}

8 个答案:

答案 0 :(得分:27)

简短的回答是不允许这样做,因为它会破坏类型的可替代性;另见Liskov Substititution Principle (LSP)

关键是Java(和其他编程语言)中的多态性依赖于您能够处理子类的实例,就像它是超类的实例一样。但是如果该方法在子类中受到限制,您会发现编译器无法确定访问规则是否允许调用方法...

例如,假设您的示例代码是合法的:

// Assume this code is in some other class ...

SuperClass s1 = new SuperClass();

s1.foo();                          // OK!

SuperClass s2 = new Subclass();

s2.foo();                          // What happens now?

SuperClass s3 = OtherClass.someMethod();

s3.foo();                          // What happens now?

如果您决定是否允许在s2.foo()的声明类型上使用s2,那么您允许从{{1}的抽象边界之外调用private方法}}。

如果您根据Subclass引用的对象的实际类型做出决定,则无法静态执行访问检查。 s2案例使这一点更加清晰。编译器完全无法知道s3返回的对象的实际类型是什么。

可能导致运行时异常的访问检查将成为Java应用程序中的主要错误来源。这里讨论的语言限制避免了这个令人讨厌的问题。

答案 1 :(得分:8)

您无法限制访问权限,因为您已在超类中允许更多访问权限。 e.g。

SuperClass sc = new SubClass();
sc.foo(); // is package local, not private.

sc的访问权限由引用sc的类型决定,而不是它引用的内容,因为编译器无法在所有情况下知道对象在运行时的类型。为了这是一个安全的假设,子类必须遵守父类给出的合同,否则它不能成为有效的子类。这与父母说实现方法但子类说它不是(或不可访问)

没有什么不同

你可以解决这个问题,说你只能通过父方法访问子类方法,而不是直接访问。这个问题是你不知道父母何时可以添加一个方法,当你创建一个方法private时,你这样做是因为你希望它是私有的,而不是另一种方式。

BTW您仍然可以通过反射访问私有方法,这会产生副作用,导致JVM出现各种问题。例如它必须保留私有方法,即使它可能确定无法正常调用它。

简而言之,您希望代码能够代表其所说的内容,而不是具有分裂的个性。它既可以是本地包,也可以是私有的,而不是介于两者之间但不是真的。从另一个角度来看,这不是一个问题。即子类是公开的。它只是意味着子类可以在比父类更多的地方使用,就像它可以实现更多的方法一样。

答案 2 :(得分:6)

如果允许这样做,就会有一个后门,通过它可以调用不应该访问的方法。

让我们说这是允许的

class Super {  
    public void method() {  
        System.out.println("Super");  
    }  
}


class Sub extends Super {  
    // This is not allowed, but suppose it was allowed  
    protected void method() {  
        System.out.println("Sub");  
    }  
}

// In another class, in another package:  
Super obj = new Sub();  
obj.method();

obj.method是可能的,因为method()在类Super中是public。但不应该允许,  因为obj实际上是指Sub的一个实例,并且在该类中,该方法受到保护!

要限制对不能从外部访问的类Sub中的方法的调用,请设置此限制。

答案 3 :(得分:1)

限制超类方法的访问修饰符是无效的覆盖,因为它破坏了超类合约并使替换原则无效,即子类对象IS-A超类对象< / em>以及。

public void doSomething(SuperClass sc) {
  sc.publicMethodInSuperClass();
}

doSomething(new SubClass()); // would break

如果允许,上述客户端代码将会中断,因为 SubClass 没有该方法 public

参考:
Liskov substitution principle

答案 4 :(得分:0)

除了使用这种结构的明显问题(正如Peter Lawrey在他的回答中指出的那样),请阅读它背后的理论:LSP,这意味着你必须能够替换主要用它的子类输入。

答案 5 :(得分:0)

我认为简短的回答是编译器编写者已经将规则设置为以这种方式工作。 LSP与手头的问题无关。

我能想到有这个限制的唯一原因是,当子类派生自接口时,作为客户端程序员,您希望能够从对派生类的引用调用接口的所有可访问方法

假设您可以编写OP已显示的代码。如果您有对派生类的引用,则应该能够调用派生类的任何公共成员(尽管在这种情况下没有)。但是,将引用作为参数传递给引用基类的方法,并且该方法将期望调用任何公共或包方法,即foo。这是其他贡献者正在寻找的LSP!

C ++示例:

class Superclass{
public:
virtual void foo(){ cout << "Superclass.foo" << endl; }
};

class Subclass: public Superclass{
virtual void foo(){ cout << "Subclass.foo" << endl; }
};

int main(){
Superclass s1;
s1.foo() // Prints Superclass.foo

Subclass s2;
// s2.foo();  // Error, would not compile

Superclass& s1a=s2; // Reference to Superclass 'pointing' to Subclass

s1a.foo(); // Compiles OK, Prints Subclass.foo()
}

答案 6 :(得分:0)

在动态方法调度中,调用重写方法在运行时而不是编译时解析。它基于通话时提到的对象......

现在假设允许较弱的访问权限,我们在您的代码中编写以下语句:

Superclass ref=new Subclass();
ref.foo()

现在在运行时,当java遇到语句ref.foo()时,它必须调用foo()的{​​{1}} ...但子类的Subclass方法被声明为你的代码中的private和private不能在它自己的类之外调用。所以现在有一个冲突,它会导致运行时异常......

答案 7 :(得分:0)

因为您更改了访问特权的顺序,所以会出现错误

正确的顺序是:==>默认为私有,保护为公共

*在您的情况下,您默认为==>私人  每当您输入错误的顺序时,结果就是错误

访问权限的正确顺序是-

`class SuperClass{
          void foo(){
    System.out.print("SuperClass");
     }
   class SubClass extends{

  //--default to default--//
         void foo(){
  System.out.print("SubClass");
   }
 //--default to protected--//

  protected void foo(){
  System.out.print("SubClass");

 //--default to public //

 public void foo(){
 System.out.print("SubClass");
 }` 

    **you make sure to preserve correct order while overriding **