重写方法是否可以具有与基 class 中的访问说明符不同的访问说明符?

Can an overriding method have a different access specifier from that in the base class?

在抽象 class 中,我必须将哪个访问修饰符用于方法, 所以 subclasses 可以决定它是否应该是 public?是否可以 "override" Java 中的修饰符?

public abstract class A {

    ??? void method();
}

public class B extends A {
    @Override
    public void method(){
        // TODO
    }
}

public class C extends B {
    @Override
    private void method(){
        // TODO
    }
}

我知道静态绑定会有问题,如果 有人打电话:

// Will work
A foo = new B()
foo.method();

// Compiler ?
A foo = new C();
foo.method();

但也许还有另一种方法。我怎样才能做到这一点?

这是 @Override 合同的一部分。

答案是:没有任何可能实现你所拥有的。

The access level cannot be more restrictive than the overridden method's access level. For example: if the superclass method is declared public then the overridding method in the sub class cannot be either private or protected.

这不只是 abstract 类 的问题,而是所有 类 和方法的问题。

重写方法时,只能将修饰符更改为更宽,反之则不行。例如,这段代码是有效的:

public abstract class A {

    protected void method();
}

public class B extends A {
    @Override
    public void method() { }
}

但是,如果您尝试缩小可见性,则会出现编译时错误:

public abstract class A {
    protected void method();
}

public class B extends A {
    @Override
    private void method() {}
}

对于你的情况,我建议 C 不实现 A,因为 A 的抽象 暗示 非私人 method():

public class C {
    private void method(){
      //TODO
    }
}

另一种选择是使 C 中的 method() 实现抛出 RuntimeException:

public class C extends A {

    @Override
    public void method(){
        throw new UnsupportedOperationException("C doesn't support callbacks to method()");
    }
}

放宽限制是可以的,但不能放宽限制:

public abstract class A {
    protected void method();
}

public class B extends A {
    @Override
    public void method(){    // OK
    }
}

public class C extends A {
    @Override
    private void method(){    // not allowed
    }
}

制作原始方法 private 也不会起作用,因为这样的方法在子类中不可见,因此不能被覆盖。

我建议使用 interfaces 来有选择地公开或隐藏方法:

public interface WithMethod {
    // other methods
    void method();
}

public interface WithoutMethod {
    // other methods
    // no 'method()'
}

public abstract class A {
    protected void method();
}

public class B extends A implements WithMethod {
    @Override
    public void method(){
      //TODO
    }
}

public class C extends B implements WithoutMethod {
    // no 'method()'
}

...然后仅通过接口使用实例。

理论:

您已确定修改器顺序:

public <- protected <- default-access X<- private

重写该方法时,可以增加但不能降低修饰符级别。例如,

public -> []
protected -> [public]
default-access -> [public, default-access]
private -> []

实践:

在你的情况下,你不能将 ??? 变成一些修饰符,因为 private 最低的 修饰符并且 private class 成员不被继承。

出于非常充分的理由,您所要求的是不可能的。

Liskov substitution principle 基本上说:一个 class S 是另一个 class T 的子 class 只有这样,当你可以替换任何出现的一些 "T object" 和一些 "S object" - 没有注意到。

如果您允许 S 将 public 方法缩减为私有,那么您不能再这样做了。因为突然之间,可以在使用某些 T 时调用的方法......不再可以在 S 上调用。

长话短说:继承不是天上掉下来的东西。它是 class 中的 属性 个, 作为程序员负责。换句话说:继承不仅仅意味着在源代码中写下 "class S extends T"!

由于多态性,这是不可能的。考虑以下。您在 class A 中使用了一些不是 private 的访问修饰符的方法。为什么不私了?因为如果它是私有的,那么其他 class 甚至都不会知道它的存在。所以它必须是别的东西,而且必须可以从某处.

访问别的东西

现在假设您将 class C 的实例传递给 某处 。但是你事先将它向上转换为 A,所以你最终会在 某处 :

void somewhereMethod(A instance) {
    instance.method(); // Ouch! Calling a private method on class C.
}

一个很好的例子是 Qt 中的 QSaveFile。与 Java 不同,C++ 实际上允许降低访问权限。所以他们就这样做了,禁止 close() 方法。他们最终得到的是一个 QIODevice subclass,它不再是真正的 QIODevice。如果您将指向 QSaveFile 的指针传递给某个接受 QIODevice* 的方法,它们仍然可以调用 close(),因为它在 QIODevice 中是 public。他们通过 QSaveFile::close()(私有的)调用 abort() 来“修复”这个问题,所以如果你这样做,你的程序会立即崩溃。这不是一个很好的“解决方案”,但没有更好的解决方案。这只是糟糕的 OO 设计的一个例子。这就是 Java 不允许的原因。

编辑

并不是我错过了您的 class 是抽象的,但我也错过了 B extends C 而不是 A 这一事实。这样你想做的事情就完全不可能了。如果该方法在 B 中是 public,那么在所有子class 中它也将是 public。您唯一可以做的就是记录它不应该被称为 并且 可能会覆盖它以抛出 UnsupportedOperationException。但这会导致与 QSaveFile 相同的问题。请记住,您的 class 的用户甚至可能不知道它是 C 的一个实例,因此他们甚至没有机会阅读它的文档。

总的来说,这只是一个非常糟糕的面向对象的想法。也许你应该问另一个关于你试图用这个层次结构解决的确切问题的问题,也许你会得到一些关于如何正确解决它的体面建议。