访问同一类中另一个对象的私有字段


91
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设计为使私有字段具有类级别的访问权限而不具有对象级别的访问权限


5
我相信OP会将传递给“ someMethod”的“ Person”对象视为一个单独的对象,因此,该方法不应访问其私有成员……即使它位于“ Person”类中。
Inisheer 2013年

1
某些语言不这样做(例如,Newspeak)。您为什么不能得到很好的答案。从碰巧指定的内容中,您将获得答案。
汤姆·哈特芬

someMethod无效。它不返回任何东西。一定是void
Nicolas Barbulesco

1
如果不是这种情况,将很难编写副本构造函数和赋值运算符imo。
rozina 2014年

Answers:


68

我对答案也很好奇。

我找到的最令人满意的答案是来自Artemix,在另一篇文章中(我将AClass重命名为Person类): 为什么要使用类级访问修饰符而不是对象级?

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

这个想法是“外部世界”不应该更改Person内部流程,因为Person实现可能随时间而变化(并且您将不得不改变整个外部世界以解决实现上的差异-这几乎是不可能的)。

当Person实例访问其他Person实例的内部时-您可以确保两个实例始终知道Person的实现细节。如果更改了Person过程的内部逻辑-您要做的就是更改Person的代码。

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


4
这可能是原因。但这是一个坏主意。这会鼓励不良做法。开发人员可以Person在类中访问的字段,Person而不必知道整个类的实现。优良作法是使用访问器,而不必知道访问器执行的操作。
Nicolas Barbulesco

16
@NicolasBarbulesco我认为答案中给出的原因是合理的。假设您要equals(Object)在Java类中实现该方法,以检查Person对象与的另一个实例是否相等Person。您可能希望使外界能够检查两个实例是否相等,但是您可能不想使用公共访问器方法向外界公开检查相等性所必需的所有类的私有字段。具有对private字段的类级别的访问权限,可以实现这种方法,而无需诱导实现此类公共方法的必要性。
Malte Skoruppa 2015年

1
@MalteSkoruppa-这是一个很好的例子(实现方法equals)。
Nicolas Barbulesco,2015年

@MalteSkoruppa-但是,equals可以通过调用私有访问器来实现该方法。
Nicolas Barbulesco 2015年

@NicolasBarbulesco当然可以,但是要点是,无论您使用私有访问器还是直接访问私有字段来实现该方法,private都必须授予类级别的访问权限。我同意使用访问器通常是一个好习惯,尽管在这种情况下,这主要取决于上下文和个人风格。请注意,Effective Java中的Joshua Bloch (项目8)和Dobbs博士的这篇文章中的Tal Cohen在讨论如何实现时都直接访问其代码清单中的私有字段equals
Malte Skoruppa 2015年

17

参见Java语言规范,第6.6.1节。确定可访问性

它指出

否则,如果声明了成员或构造函数private,则仅当访问发生在包含成员或构造函数的声明的顶级类(第7.6节)的主体内时,才允许访问。

单击上面的链接以获取更多详细信息。答案是:因为James Gosling和Java的其他作者决定采用这种方式。


16

好问题。似乎对象级别访问修饰符将进一步实施封装原理。

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

因此,封装并不意味着对世界其他所有地区都封闭。这意味着要对要公开的对象保持选择性。


2
这个答案需要投票!其他答案只是重申了“规则”,但只有这一点才能真正揭示规则背后的原因并达到目的。
mzoz

4
但是,让一个对象负责提供其自身的副本不是更好吗?然后,如果您需要对象的深层副本,那么您是同一类的另一个对象还是其他类的对象都没关系:它是相同的机制,o.deepCopy()或其他任何东西。
土边

4

这是有效的,因为您处于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-我们的大数量级。


这类似于依赖注入。您还可以在其中注入其他类的对象,而在该对象中不能访问私有变量。
Nageswaran 2013年

当然,如果您试图从类B的对象构造类A,并且B具有私有组件,那么要么需要将A声明为朋友,要么只能看到公共部分[如果A源自B,则可能受到保护。 ]。
Mats Petersson

在java和.net中,没有friend的概念。在这种情况下,该如何处理?
Nageswaran 2013年

1

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

我要说,便利似乎是这里的关键。实际上,在OP所示的场景中,对象级别的私有可见性将不得不将方法公开给其他类(例如,在同一包中)。

实际上,我既无法构思也无法找到一个示例,说明与对象私有级的可见性相比,类私有级的可见性(如Java提供的)会引起任何问题。

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

例如Eiffel提供选择性导出:您可以将任何类特征导出到您选择的任何类,从{NONE}(对象专用)到{ANY}(等效于public,也是默认值),再到{PERSON} (类私有),请参阅特定类别的类{PERSON,BANK}。

有趣的是,在Eiffel中,您不需要将属性设为私有,也不需要编写getter来防止其他类对其进行赋值。默认情况下,埃菲尔中的公共属性在只读模式下可访问,因此您不需要获取方法即可返回其值。

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


0

因为private access修饰符使它仅在class中可见。此方法仍在该类中


我的问题是,为什么将它设计为“在类内”,但为什么不设计为“仅在对象内”呢?
Nageswaran

因为那是java的制作方式。
darijan

为什么用Java这样制作Java?
Nageswaran 2014年

Java没有做到,Java创造者做到了。为什么?因为他们摇滚!
darijan 2014年

Java创建者不会无缘无故地这样做,而且一定有一定的理由这样做;我问的原因
Nageswaran

0

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


-1

我们首先要了解的是,我们要做的就是必须遵循oops原则,因此封装就是将数据包装在包(即类)中,而不是将所有数据表示为Object且易于访问。因此,如果我们将字段设为非私有字段,则该字段会被单独访问。并导致有害的寄生虫。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.