覆盖Java中的私有方法

Bil*_*ill 58 java inheritance private-methods

正如这里简洁描述的那样,在Java中覆盖私有方法是无效的,因为父类的私有方法是"自动最终的,并且从派生类中隐藏".我的问题主要是学术问题.

如何不允许父级的私有方法被"覆盖"(即,在子类中使用相同的签名独立实现),是不是违反封装?根据封装原则,子类无法访问或继承父级的私有方法.它是隐藏的.

那么,为什么要限制子类使用相同的名称/签名实现自己的方法呢?有没有一个很好的理论基础,或者这只是某种实用的解决方案?其他语言(C++或C#)对此有不同的规定吗?

Jon*_*eet 70

您不能覆盖私有方法,但您可以在派生类中引入一个没有问题.编译好:

class Base
{
   private void foo()
   {
   }
}

class Child extends Base
{
    private void foo()
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果您尝试应用@Override注释,Child.foo()则会出现编译时错误.只要你有你的编译器/ IDE设置,给你警告或错误,如果你缺少一个@Override注释,都应该很好.不可否认,我更喜欢C#override作为关键字的方法,但在Java中这样做显然为时已晚.

至于C#处理"覆盖"私有方法 - 私有方法首先不能是虚方法,但你肯定可以在基类中引入一个与私有方法同名的新私有方法.

  • (+ 1),虽然我不喜欢新卷曲的支架 (35认同)
  • 应该注意的是,如果有一个方法`Base.boo()`调用`foo()`,那么它将调用`Base.foo()`而不是`Child.foo()` (19认同)
  • @Gregory:我不明白为什么.在这种情况下,我正在纠正OP的误解 - 他认为你做不了什么事实上你可以,这意味着没有"为什么"回答. (6认同)
  • 据我所知,如果你坚持在你的子类中的方法上放置一个`@ override`注释,你只会得到一个错误. (2认同)
  • 你的回答让我想起了http://stackoverflow.com/questions/1953530/why-does-java-prohibit-static-fields-in-inner-classes/1953580#1953580的答案 - 大多数答案只是陈述事实"没有你不能"并解释你可以做些什么来解决这些事实,但他们并没有真正试图回答这个问题"为什么这样决定呢?" (2认同)

Kon*_*lph 27

好吧,允许私有方法被覆盖将导致封装泄漏或安全风险.如果我们假设它是可能的,那么我们会得到以下情况:

  1. 假设有一个私有方法,boolean hasCredentials()那么扩展类可以简单地覆盖它,如下所示:

    boolean hasCredentials() { return true; }
    
    Run Code Online (Sandbox Code Playgroud)

    从而打破了安全检查.

  2. 原始类防止这种情况的唯一方法是声明其方法final.但是现在,这是通过封装泄漏实现信息,因为派生类现在不能再创建一个方法hasCredentials- 它将与基类中定义的方法冲突.

    这很糟糕:让我们说这个方法最初并不存在Base.现在,实现者可以合法地派生一个类,Derived并为它提供一个hasCredentials按预期工作的方法.

    但现在,发布了原版的版本Base.它的公共接口不会改变(也不会改变它的不变量)所以我们必须期望它不会破坏现有的代码.只有它,因为现在与派生类中的方法存在名称冲突.

我认为问题源于一个误解:

如何/不/违反封装不允许父进程的私有方法被"覆盖"(即,在子类中使用相同的签名独立实现)

括号内的文本与之前的文本相反.Java 确实允许你"在子类中独立地实现具有相同签名的[私有方法]".如上所述,不允许这样做会违反封装.

但是"不允许父母的私有方法被"覆盖""是不同的,并且是确保封装的必要条件.


Gre*_*osz 16

"其他语言(C++或C#)对此有不同的规定吗?"

好吧,C++有不同的规则:静态或动态成员函数绑定过程和访问权限强制是正交的.

为成员函数提供private访问权限修改意味着此函数只能由其声明类调用,而不能由其他函数调用(甚至不是派生类).当您将private成员函数声明为virtual纯虚拟(virtual void foo() = 0;)时,您允许基类从专业化中受益,同时仍然强制执行访问权限.

virtual成员函数方面,访问权限告诉您应该执行的操作:

  • private virtual 意味着您可以专门化行为,但成员函数的调用是由基类进行的,当然是以受控的方式
  • protected virtual 意味着您应该/必须在覆盖它时调用成员函数的上层版本

因此,在C++中,访问权限和虚拟性是相互独立的.确定函数是静态还是动态绑定是解析函数调用的最后一步.

最后,模板方法设计模式应优先于public virtual成员函数.

参考:对话:几乎是你的

本文给出了private virtual成员函数的实际用法.


ISO/IEC14882-2003§3.4.1

如果名称查找名称是函数名称,则名称查找可以将多个声明与名称相关联; 据说声明形成一组重载函数(13.1).名称查找成功后,将发生重载分辨率(13.3).访问规则(第11节)仅在名称查找和功能重载解析(如果适用)成功后才被考虑.只有在名称查找之后,函数重载解析(如果适用)和访问检查成功才会在表达式处理中进一步使用名称声明引入的属性(第5节).

ISO/IEC14882-2003§5.2.2

在成员函数调用中调用的函数通常根据对象表达式的静态类型(第10节)进行选择,但如果未使用aqualified-idthen指定该函数是virtualand,则实际调用的函数将是最终覆盖(10.3)对象表达式的动态类型中的所选函数[注意:动态类型是由对象表达式的当前值指向或引用的对象的类型.


Mic*_*rdt 7

父类的私有方法不能被子类访问或继承,内联封装原则.它是隐藏的.

那么,为什么要限制子类使用相同的名称/签名实现自己的方法呢?

没有这样的限制.你可以毫无问题地做到这一点,它不会被称为"重写".

重写的方法受动态调度的影响,即实际调用的方法是在运行时根据调用它的对象的实际类型选择的.使用私有方法,这不会发生(根据您的第一个语句,不应该发生).这就是声明"私有方法无法被覆盖"的含义.