在Java中重写私有方法

在Java中重写私有方法,java,inheritance,private-methods,Java,Inheritance,Private Methods,正如简要描述的,在Java中重写私有方法是无效的,因为父类的私有方法是“自动最终的,并且对派生类隐藏”。我的问题主要是学术性的 不允许父类的私有方法被“重写”(即在子类中使用相同的签名独立实现),这怎么不违反封装?根据封装原则,父类的私有方法不能被子类访问或继承。它是隐藏的 那么,为什么要限制子类使用相同的名称/签名实现自己的方法呢?这是否有很好的理论依据,或者这只是一种务实的解决方案吗?其他语言(C++或C#)对此有不同的规则吗?您不能重写私有方法,但可以在派生类中引入私有方法而不会出现问题。

正如简要描述的,在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();
        }
    }