【问题标题】:In Java, why can't a super-class method access protected or private methods/variables from a sub-class instance?在 Java 中,为什么超类方法不能从子类实例访问受保护或私有方法/变量?
【发布时间】:2010-10-28 07:36:59
【问题描述】:

让我们从另一种行为开始:即使您将方法/变量声明为私有,同一类的另一个实例也可以访问它。没关系,我可以忍受它。我将这些称为类私有而不是实例私有。

现在是问题部分: 例如,在运行时,我希望能够检查 this 类中的所有 String 变量是否不为空,如果它们为空,则应将其更改为字符串“NULL”。

我可以使用反射来遍历变量并获取它们的值。但是,如果我扩展我的类并添加私有甚至受保护的变量,我的基类将无法访问它们。我必须在变量上 setAccessible 才能使用它们。

所以请向我解释为什么基类(超类)不能从其子类访问私有/受保护的变量。这是它的子类,所以我不明白。这背后的想法是什么?

我知道超类不应该知道它的子类,但在我的例子中它是有道理的,不是吗?

是因为我不能或不应该以这种方式限制我的子类吗?


更新: 根据答案,我还想知道:为什么不从同一个类访问另一个实例的私有变量被视为违反封装?

【问题讨论】:

    标签: java oop class inheritance instance


    【解决方案1】:

    这很简单,因为它违反了封装。另一个班级不应该能够进入你的班级并搞乱事情,即使你概括了那个班级。例如,车辆如何知道有关汽车的任何信息?基类的全部意义在于提供子类,但就像过度保护的父母一样,您的建议太过分了。

    【讨论】:

    • 那么为什么从同一个类实例访问另一个实例的私有变量不被认为是违反封装的呢?汽车应该能够限制宝马只有轮子而不是机翼,因为如果宝马有机翼,它就不是汽车(即使它“内部”使用机翼)
    • 因为我是班级。你不能在课外做你所说的事情,因此封装是机智的。在类中,您可以修改该类的另一个实例的私有成员。怎么就违反了?我在课堂上!我可以更改与我想要的课程相关的任何内容,为什么不允许呢?
    【解决方案2】:

    要回答您的更新问题(因为原始问题已经得到很好的回答),私有的目的是隐藏实现细节,以便这些实现细节不会成为依赖项。这是面向对象编程的精髓 - 封装通过将不同部分隔离到各自的区域来确保复杂性保持可控。

    由于类知道它自己的实现——它定义了它,因此限制对其他实例的访问没有任何好处,因为类实现声明了所有私有实例,它已经公开了所有这些细节。

    在这种情况下隐藏它实际上会增加复杂性,因为您必须添加一个额外的访问级别以允许类级别的可见性而不是实例级别的可见性,而实际上并没有真正封装任何进一步的复杂性。

    【讨论】:

      【解决方案3】:

      这都是关于继承和封装的。

      Java 的可见性规则规定

      1. 私有成员只能在定义它们的类中访问
      2. 受保护的成员只能在
        • 定义它们的类
        • 定义它们的类的子类
        • 与定义它们的类位于同一包中的其他类

      当然,正如您所提到的,在反思中您可以更改规则(除非 SecurityManager 禁止)

      【讨论】:

      • @Tetsujin:你让我笑了;)
      • 那为什么从同一个类实例访问另一个实例的私有变量不被认为是违反封装?
      • @Tetsujin 同意,我问为什么设计是这样的?为什么在这种情况下使用 new 修饰符会很糟糕:unprotected,不公开。
      • Java 中的封装方案不是实例级封装;它是类级别的封装。这正是大多数面向对象语言的实现方式。这不是一个真正的问题,因为单个类正在控制其所有实例中的数据。
      【解决方案4】:

      我更倾向于从务实和现实的角度看待它:

      public class Test1 {
          private int a = 1;
      
          public static void main(String s[]) {
              System.out.println((new Test1()).a);
              System.out.println((new Test1()).getA());
              System.out.println((new Test2()).getA());
          }
      
          public int getA() { return a; }
      }
      
      class Test2 extends Test {
          private int a = 2;
      
          public int getA() { return a; }
      }
      

      您建议Test1 使用什么语法来访问Test2 中的私有成员a。当Test1被写入时,Test2可能甚至都不存在。

      同样,基类(超类)不知道它是从什么时候继承而来的。为了让基类知道它何时被继承,您必须能够在从它继承时修改基类。您是否建议我在写作时以某种方式修改 ArrayList 的本地副本:

      class MyArrayList extends ArrayList
      

      当我决定在你的计算机上运行我的代码的那一天会发生什么,你会得到我修改后的 ArrayList 的副本,它知道 MyArrayList,还是我会以某种方式修改你计算机上的副本?

      能否以某种方式克服这些问题?当然。允许它有什么价值吗?什么都看不到。

      【讨论】:

      • 在您的示例中,Test2 实例实际上有两个变量:Test1.a 和 Test2.a,每个变量都只能从它的类访问。 Test1 可以更改 Test2 实例中 Test1.a 的值,这被语言认为是可以的,但对 Test2 没有影响,即使 Test1 “认为”它确实如此。当子类用作基类时,有很多例子会破坏兼容性。这和我问的有什么不同?
      • 我不确定你想说什么。我回到我最初的问题:1)Test2 可以使用什么样的语法来访问或更新其父类的私有成员? 2) Test1 如何知道如何将其内部成员暴露给一个甚至可能还没有编写的类? 3) 您如何将更改传播到已修改以提供对继承子项的访问的基类? 4) 最重要的是,这可以为程序员提供什么可能的价值?
      • 1) Test2 无法访问父级的私有成员(这就是受保护的修饰符的用途)。但是Test1 可以访问Test1 的“未受保护”(非私有)成员。 2)受保护的 3)这与更改基类中的受保护或私有函数相同 4)我会写另一个问题来说明确切的问题
      猜你喜欢
      • 2015-08-14
      • 2014-08-16
      • 2017-12-09
      • 2011-10-06
      • 1970-01-01
      • 2020-04-23
      • 1970-01-01
      • 2011-12-20
      • 2013-11-25
      相关资源
      最近更新 更多