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

2022-09-03 16:42:12

我有一个类,它有一个方法,默认情况下,其访问说明符是公共的。现在,我想在子类中扩展这个类,并且我想重写此方法以获得访问说明符“private”。编译此代码时,我收到编译错误:

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

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

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

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

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

答案 1

简短的回答是,这是不允许的,因为它会破坏类型的可替代性;另见里氏替代原则(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()s2privateSubclass

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

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


答案 2

您无法限制访问,因为您已经在超级类中允许了更多访问权限,例如

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

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

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

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

简而言之,你想要代码,它意味着什么,而不是人格分裂。它要么是本地软件包,要么是私有的,不是介于两者之间的东西,但也不是真正的。另一方面,这不是一个问题。即,如果子类是公共的。它只是意味着子类可以在比父类更多的地方使用,就像它可以实现更多的方法一样。


推荐