Java 为什么可以';我们在子类中分配较弱的特权吗

Java 为什么可以';我们在子类中分配较弱的特权吗,java,inheritance,Java,Inheritance,我有一个类,它有一个默认访问说明符为public的方法。现在,我想在一个子类中扩展这个类,我想重写这个方法,使访问说明符为“private”。编译此代码时,我遇到一个编译错误: class Superclass { void foo() { System.out.println("Superclass.foo"); } } class Subclass extends Superclass { private void foo()

我有一个类,它有一个默认访问说明符为public的方法。现在,我想在一个子类中扩展这个类,我想重写这个方法,使访问说明符为“private”。编译此代码时,我遇到一个编译错误:

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

class Subclass extends Superclass 
{
    private void foo() 
    {
        System.out.println("Subclass.foo");
    }
}
“正在尝试分配较弱的访问权限”

有人能给我解释一下在子类中分配较弱的特权有什么不对吗

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

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

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

简而言之,这是不允许的,因为它会破坏类型的可替换性;另见

关键是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
类型决定是否允许
s2.foo()
,则允许从
子类
的抽象边界之外调用
私有
方法

如果您根据
s2
引用的对象的实际类型做出决定,则无法静态执行访问检查。
s3
案例更清楚地说明了这一点。编译器绝对无法知道
someMethod
返回的对象的实际类型


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

您不能限制访问,因为您已经在超类中允许了更多的访问。e、 g

SuperClass sc = new SubClass();
sc.foo(); // is package local, not private.
sc
的访问由引用的类型决定,而不是由引用的内容决定,因为编译器不可能在所有情况下都知道对象在运行时的类型。为了使这成为一个安全的假设,子类必须遵守父类给出的合同,否则它不能成为有效的子类。这与父类表示方法已实现,而子类表示方法未实现(或不可访问)没有什么不同

您可以通过说只能通过父类而不是直接访问子类方法来解决这个问题。问题是,您不知道父级何时可能添加方法,而当您将方法设置为私有时,您这样做是因为您希望它是私有的,并且无法以其他方式访问

顺便说一句,您仍然可以通过反射访问私有方法,反射的副作用是它会给JVM带来各种各样的问题。e、 g.它必须保留私有方法,即使它可能确定无法正常调用它


简言之,您需要的代码意味着它所说的内容,而不是人格分裂。它要么是本地包,要么是私有包,不是介于两者之间的某种东西,但也不是真的。从另一方面来说,这不是一个问题。i、 e.如果子类是公共的。这只是意味着子类可以在比父类更多的地方使用,就像它可以实现更多的方法一样。

除了使用这种结构的明显问题(正如Peter Lawrey在他的回答中指出的),还可以阅读它背后的理论:,这意味着您必须能够用它的子类替换主类型。

限制超类方法的访问修饰符是无效的重写,因为它破坏了超类契约并使替换原则无效,即子类对象也是超类对象

如果这是允许的,那么上面的客户机代码将被破坏,因为您的子类没有public方法

参考资料:

如果允许,将有一个后门,您可以通过它调用不应访问的方法

让我们说这是允许的

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类中不应从外部访问的方法的调用,需要设置此限制

我认为简单的答案是,编译器编写者已经制定了这样的工作规则。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()
}
类超类{
公众:

虚拟void foo(){cout在动态方法分派中,对重写方法的调用在运行时而不是编译时解析。它基于调用时引用的对象

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

Superclass ref=new Subclass();
ref.foo()
现在,在运行时,当java遇到语句
ref.foo()
,它将不得不调用
Subclass
foo()
…但是子类的
foo()
方法在代码中声明为private,private不能在它自己的类之外调用..所以
`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 **