【问题标题】:Access private field of another object in same class访问同一类中另一个对象的私有字段
【发布时间】:2013-06-06 07:13:37
【问题描述】:
class Person 
{
   private BankAccount account;

   Person(BankAccount account)
   {
      this.account = account;
   }

   public Person someMethod(Person person)
   {
     //Why accessing private field is possible?

     BankAccount a = person.account;
   }
}

请忘记设计。我知道 OOP 指定私有对象是类私有的。我的问题是,为什么 OOP 被设计成私有字段具有类级别的访问权限,而 不是对象级别的访问权限?

【问题讨论】:

  • 我相信 OP 认为传递给“someMethod”的“Person”对象是一个单独的对象,因此该方法不应访问它的私有成员......即使它在“人”类。
  • 有些语言不这样做(例如新话)。你不太可能得到一个很好的答案。您会从指定的内容逆向获得答案。
  • someMethod 无效。它不返回任何东西。必须是void。
  • 如果不是这样的话,复制构造函数和赋值运算符imo就很难写了。
  • 在 Scala 中,您可以指定 private[this]。这是我猜 Java 永远不会添加的 Scala 功能。但是,我之所以遇到这个问题,是因为我遇到了一个 IDE 故障,告诉我实例 A 无法访问实例 B 的类私有字段,即使它们是同一个类。我保存了,光没有穿过黄色就从红色变成绿色。

标签: java c++ .net oop


【解决方案1】:

因为private 访问修饰符使其仅在类中可见。这个方法还在类中。

【讨论】:

  • 我的问题是为什么它被设计成“类内”但为什么不像“只在对象内”?
  • 因为 java 就是这样制作的。
  • 而java为什么会这样?
  • Java 没有成功,Java 创造者做到了。为什么?因为他们摇滚!
  • Java 创造者不会无缘无故地这样做,而且这样做肯定是有原因的;我在问原因
【解决方案2】:

private 字段可在声明该字段的类/对象中访问。它对于它所在的类/对象之外的其他类/对象是私有的。

【讨论】:

    【解决方案3】:

    这是有效的,因为您在class Person 中 - 允许一个类在它自己的类中插入。当你想编写一个复制构造函数时,这真的很有帮助,例如:

    class A
    {
       private:
          int x;
          int y;
       public:
          A(int a, int b) x(a), y(b) {}
          A(A a) { x = a.x; y = y.x; }
    };
    

    或者,如果我们想为我们的大数字类编写 operator+ 和 operator-。

    【讨论】:

    • 这类似于依赖注入。您还可以在其中注入一些其他类的对象,您无法访问私有变量。
    • 当然,如果你试图从 B 类的对象构造一个 A 类,并且 B 有私有组件,那么要么 A 需要被声明为朋友,要么只能看到公共部分 [可能受保护,如果 A 派生自 B]。
    • 在 java 和 .net 中没有朋友的概念。遇到这种情况,如何处理?
    【解决方案4】:

    见Java Language Specification, Section 6.6.1. Determining Accessibility

    说明

    否则,如果成员或构造函数声明为private,则 当且仅当访问发生在 包含成员声明的顶级类(第 7.6 节)或 构造函数。

    点击上面的链接了解更多详情。所以答案是:因为 James Gosling 和 Java 的其他作者决定这样做。

    【讨论】:

    • OP 的问题并非特定于 Java,而是关于许多 OOP 语言的这种设计选择的哲学。这个答案只是重新陈述了事实,没有解释为什么会做出这样的决定。
    【解决方案5】:

    我也有点好奇这个答案。

    我找到的最令人满意的答案来自 Artemix 的另一篇文章(我正在用 Person 类重命名 AClass): Why have class-level access modifiers instead of object-level?

    private 修饰符强制执行封装原则。

    这个想法是“外部世界”不应该对 Person 内部流程进行更改,因为 Person 实现可能会随着时间而改变(并且您必须改变整个外部世界以修复实现中的差异 - 这几乎是不可能的) .

    当 Person 实例访问其他 Person 实例的内部时 - 您可以确定两个实例总是知道 Person 的实现细节。如果 Person 流程的内部逻辑发生了变化,您只需更改 Person 的代码即可。

    编辑: 请投票 Artemix 的回答。我只是复制粘贴而已。

    【讨论】:

    • 这可能是原因。但这是一个坏主意。这会鼓励不良做法。在类Person 中访问Person 字段的开发人员不必知道整个类的实现。好的做法是使用访问器,而不必知道访问器做了什么操作。
    • @NicolasBarbulesco 我认为答案中给出的原因是合理的。例如,假设您想在 Java 类中实现 equals(Object) 方法来检查 Person 对象与 Person 的另一个实例的相等性。您可能希望让外部世界检查两个实例是否相等,但您可能不希望使用公共访问器方法向外部世界公开检查相等性所需的所有类的私有字段。拥有对 private 字段的类级别访问权限,可以实现这样的方法,而无需实现这样的公共方法。
    • @MalteSkoruppa - 这是一个很好的例子(实现方法equals)。
    • @MalteSkoruppa - 但是,可以通过调用私有访问器来实现方法 equals。
    • @NicolasBarbulesco 当然是的,但关键是,无论您使用私有访问器还是直接访问私有字段来实现该方法,private 都必须授予类级别的访问权限。我同意使用访问器通常是一个好习惯,尽管在这种情况下,它主要是上下文和个人风格的问题。请注意,Effective Java(第 8 项)中的 Joshua Bloch 和 this Dr. Dobbs article 中的 Tal Cohen 在讨论如何实现 equals 时直接访问其代码清单中的私有字段。
    【解决方案6】:

    好问题。对象级访问修饰符似乎会进一步执行封装原则。

    但实际上情况恰恰相反。让我们举个例子。假设您想在构造函数中深度复制一个对象,如果您无法访问该对象的私有成员。那么唯一可能的方法是向所有私有成员添加一些公共访问器。这将使您的对象裸露到系统的所有其他部分。

    因此,封装并不意味着对世界其他地方都关闭。这意味着要对你想向谁开放。

    【讨论】:

    • 这个答案需要投票!其他答案只是重新陈述“规则”,但只有这个答案真正揭示了规则背后的原因并切中要害。
    • 但是让一个对象负责提供自己的副本不是更好吗?然后,如果您需要一个对象的深层副本,无论您是同一类的另一个对象还是不同类的对象都没有关系:这是相同的机制,o.deepCopy() 或其他。
    【解决方案7】:

    在 Java 中使用反射概念可以修改私有字段和方法

    Modificando metodos y campos privados con Refleccion en Java

    【讨论】:

    • 这与那个问题没有任何关系
    【解决方案8】:

    在这里我们首先要了解的是,我们要做的就是必须遵循 oops 原则,因此封装就是说 将数据包装在包(即类)中,然后将所有数据表示为对象并且易于访问。 所以如果我们将字段设为非私有 它是单独访问的。这会导致糟糕的行为。

    【讨论】:

      【解决方案9】:

      关于为什么 Java 中私有可见性的语义是类级别而不是对象级别的问题,我只需 2 美分。

      我想说方便似乎是这里的关键。事实上,在 OP 说明的场景中,对象级别的私有可见性将强制将方法暴露给其他类(例如,在同一个包中)。

      事实上,与对象私有级别的可见性相比,我既无法编造也无法找到一个示例,表明类私有级别的可见性(如 Java 提供的)会产生任何问题。

      也就是说,具有更细粒度的可见性策略系统的编程语言可以提供对象级别和类级别的对象可见性。

      例如Eiffel,提供选择性导出:您可以将任何类功能导出到您选择的任何类,从 {NONE}(对象私有)到 {ANY}(相当于公共,也是默认值) ,到 {PERSON}(私有类,参见 OP 的示例),到特定的类组 {PERSON, BANK}。

      有趣的是,在 Eiffel 中,您不需要将属性设为私有并编写 getter 来防止其他类分配给它。默认情况下,Eiffel 中的公共属性可在只读模式下访问,因此您不需要 getter 来返回它们的值。

      当然,您仍然需要一个 setter 来设置属性,但您可以通过将其定义为该属性的“分配器”来隐藏它。如果您愿意,这允许您使用更方便的赋值运算符而不是 setter 调用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-05
        • 1970-01-01
        • 2013-05-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多