在Java中重写私有方法
正如简要描述的,在Java中重写私有方法是无效的,因为父类的私有方法是“自动最终的,并且对派生类隐藏”。我的问题主要是学术性的 不允许父类的私有方法被“重写”(即在子类中使用相同的签名独立实现),这怎么不违反封装?根据封装原则,父类的私有方法不能被子类访问或继承。它是隐藏的 那么,为什么要限制子类使用相同的名称/签名实现自己的方法呢?这是否有很好的理论依据,或者这只是一种务实的解决方案吗?其他语言(C++或C#)对此有不同的规则吗?您不能重写私有方法,但可以在派生类中引入私有方法而不会出现问题。这很好:在Java中重写私有方法,java,inheritance,private-methods,Java,Inheritance,Private Methods,正如简要描述的,在Java中重写私有方法是无效的,因为父类的私有方法是“自动最终的,并且对派生类隐藏”。我的问题主要是学术性的 不允许父类的私有方法被“重写”(即在子类中使用相同的签名独立实现),这怎么不违反封装?根据封装原则,父类的私有方法不能被子类访问或继承。它是隐藏的 那么,为什么要限制子类使用相同的名称/签名实现自己的方法呢?这是否有很好的理论依据,或者这只是一种务实的解决方案吗?其他语言(C++或C#)对此有不同的规则吗?您不能重写私有方法,但可以在派生类中引入私有方法而不会出现问题。
class Base
{
private void foo()
{
}
}
class Child extends Base
{
private void foo()
{
}
}
请注意,如果尝试将@Override
注释应用于Child.foo()
,则会出现编译时错误。只要将编译器/IDE设置为在缺少@Override
注释时向您发出警告或错误,一切都应该正常。诚然,我更喜欢C#方法,将覆盖作为一个关键字,但在Java中这样做显然为时已晚
至于C#对“重写”私有方法的处理——私有方法首先不能是虚拟的,但您肯定可以在基类中引入一个与私有方法同名的新私有方法。那么,允许重写私有方法将导致封装泄漏或安全风险。如果我们假设它是可能的,那么我们会得到以下情况:
假设有一个私有方法boolean hasCredentials()
boolean hasCredentials() { return true; }
从而破坏了安全检查
原始类防止这种情况的唯一方法是声明其方法final
。但现在,这是通过封装泄漏实现信息,因为派生类现在无法再创建方法hascentries
——它将与基类中定义的方法冲突
这很糟糕:假设这个方法一开始在Base
中不存在。现在,实现者可以合法地派生一个类Derived
,并给它一个方法hascentries
,该方法按预期工作
但是现在,原始Base
类的新版本发布了。它的公共接口不会改变(其不变量也不会改变),因此我们必须期望它不会破坏现有代码。只有这样,因为现在派生类中的方法与名称冲突
我认为这个问题源于一种误解:
不允许父类的私有方法被“重写”(即,在子类中使用相同的签名独立实现)是如何违反封装的
括号内的文本与其前面的文本相反。Java确实允许您“在子类中使用相同的签名独立实现[私有方法]”。如上所述,不允许这样做将违反封装
但是“不允许父级的私有方法被“重写”是不同的,并且是确保封装所必需的。类由它提供的方法以及它们的行为来定义。而不是如何在内部实现(例如,通过调用私有方法)
因为封装与行为有关,而与实现细节无关,所以私有方法与思想封装无关。从某种意义上说,你的问题毫无意义。这就像在问“在咖啡中加入奶油怎么不违反封装?”
可能私有方法是由公共的东西使用的。你可以忽略这一点。这样做,你改变了行为
父类的私有方法不能被子类访问或继承,这与封装原则是一致的。它是隐藏的
那么,为什么儿童班要被取消呢
限制实施自己的
具有相同名称/签名的方法
没有这样的限制。你可以毫无问题地做到这一点,它只是不被称为“覆盖”
被重写的方法受动态调度的约束,即实际调用的方法在运行时根据其调用的对象的实际类型进行选择。对于private方法,这不会发生(根据您的第一条语句,也不应该发生)。这就是“私有方法不能被重写”这句话的意思。我认为你误解了那篇文章的意思。这并不是说子类“被限制用相同的名称/签名实现自己的方法”
以下是经过轻微编辑的代码:
public class PrivateOverride {
private static Test monitor = new Test();
private void f() {
System.out.println("private f()");
}
public static void main(String[] args) {
PrivateOverride po = new Derived();
po.f();
});
}
}
class Derived extends PrivateOverride {
public void f() {
System.out.println("public f()");
}
}
引述如下:
您可以合理地期望输出为“public f()”
使用该引号的原因是变量po
实际上包含派生的实例。但是,由于该方法定义为私有,编译器实际上查看变量的类型,而不是对象的类型。它将方法调用转换为invokespecial(我认为这是正确的操作码,还没有检查JVM规范),而不是invokeinstance。当方法是私有的时,它的子级不可见。因此,没有凌驾于此的意义
“其他语言(C++或C#)对此有不同的规则吗?”
是,C++有不同的规则:静态成员或动态成员函数绑定过程和访问权限执行是正交的。
为成员函数提供
private
访问权限修改器意味着该函数只能由其声明的cla调用
class Base {
public void callFoo() {
foo();
}
private void foo() {
}
}
class Child extends Base {
private void foo() {
}
}
Child c = new Child();
c.callFoo();
class B {
private int foo()
{
return 42;
}
public int bar()
{
return foo();
}
}
class D extends B {
private int foo()
{
return 43;
}
public int frob()
{
return foo();
}
}