不返回对象的getter方法允许调用者直接访问成员变量吗?

huangapple go评论56阅读模式
英文:

Don't getters returning objects allow callers direct access to member variables?

问题

让我们假设我有一个简单的类,它将用户的朋友存储在一个字符串ArrayList中,并提供一个getter方法来访问该ArrayList:

public class User
{
  private ArrayList<String> mFriends;
  
  // ...其他代码,如构造函数和setter方法...

  public ArrayList<String> getFriends()
  {
    return mFriends;
  }
}

由于Java和许多其他语言都是传引用方式(类似地),这是否不允许调用getFriends()的直接访问我的字符串ArrayList,即它们可以修改它,甚至不调用我的setter方法?

我感到这严重违反了封装的概念。除了动态创建一个具有相同值的新ArrayList并返回它之外,是否还有正常的解决方法,或者是我误解了什么?

编辑:我明白Java不是真正的传引用,而是传递原始对象地址的副本,但当将对象暴露给类外部的代码时,这与传引用具有相同的效果。

英文:

Let's say I have a simple class that stores a user's friends in an ArrayList of strings, with a getter to access that ArrayList:

public class User
{
  private ArrayList&lt;String&gt; mFriends;
  
  // ...other code like constructors and setters...

  public ArrayList&lt;String&gt; getFriends()
  {
    return mFriends;
  }
}

Since Java and many other languages are (equivalently) pass-by-reference, does this not allow the caller of getFriends() direct access to my ArrayList of strings i.e. they could modify it without even calling my setter?

I feel this immensely breaks the concept of encapsulation. Is there a normal workaround for this, besides dynamically creating a new ArrayList with the same values and returning that, or am I misunderstanding something?

Edit: I understand that Java is not truly pass-by-reference but rather passes over a copy of the address of the original object, but this has the same effect as pass-by-reference when exposing objects to those outside your class

答案1

得分: 7

  1. Java 不实现 按引用传递 机制,它一直是 按值传递
  2. 你描述的问题被称为 Reference Escape,是的,你说得对,调用者可以通过 引用 修改你的对象;
  3. 为了避免 Reference Escape 问题,你可以选择:
    1. 返回对象的深拷贝(使用 .clone() 方法);
    2. 使用现有数据创建一个新对象(例如 new ArrayList&lt;&gt;(yourObjectHere));
    3. 或者想出其他方法,还有其他方式可以做到这一点;
  4. 这并不真正破坏封装,本质上 来说,这更多是关于正确的设计如何实现封装;
  5. 你对性能的担忧:不,这不会影响性能,而且与性能无关;相反,这涉及到面向对象编程的 正确设计,要么是对象的 可变性,要么是 不可变性。如果你总是返回深拷贝而不是引用,那么你就无法很好地掌握你的对象。

    以你的例子为例:如果你想要更改对象的状态,而不只是通过其 setter 设置一个新对象,该怎么办?如果你想要修改现有的朋友(在你的例子中是这样的),你认为创建一个新的朋友列表并将其设置到对象中会更好吗?不,后一种情况下,你只是失去了对你的对象的控制。
英文:
  1. Java is not, and has never been implementing Pass by Reference mechanism, it has always been Pass by Value;
  2. The problem you are describing is known as Reference Escape, and yes, you are right, caller can modify your object, if you expose it via reference;
  3. In order to avoid the Reference Escape problem, you can either:
    1. return a deep copy of the object (with .clone());
    2. create a new object with the existing data (e.g. new ArrayList&lt;&gt;(yourObjectHere));
    3. or come up with some other idea, there are some other ways too do this;
  4. This does not really break the Encapsulation, per se, it is rather a point of correct design how you implement the encapsulation;
  5. Your concern about performance: no, it is not going to break performance, moreover - it has nothing to do with performance; rather it is a matter of proper design of the OOP, mandating either mutability or immutability of the object. If you were to always return a deep copy instead of reference, you would not have a chance to have a good leverage of your object.
    <br>Taking your example: what if you want to change the state of the object without just setting a new object via its setter? what if you want to amend the existing friends (which is in your example)? do you think it is rather better to create a new List of friends and set it into the object? no, you are simply losing control over your object in the latter case.

答案2

得分: 1

以下是翻译好的部分:

如果您担心封装性,那么您可以返回您的列表的副本,例如:

public ArrayList<String> getFriends() {
    return new ArrayList<>(mFriends);
}

顺便说一下,Java 不是真正的按引用传递,而更像是按值传递。

英文:

If you are worried about encapsulation then you can return a copy of your list e.g.

public ArrayList&lt;String&gt; getFriends() {
    return new ArrayList&lt;&gt;(mFriends);
  }

By the way, Java is not truly pass-by-reference it's more pass-by-value.

答案3

得分: 1

你说的对于可变对象是正确的。你可以使用 Collections.unmodifiableList 来包装字段等。还有一种常见的做法是对于(可变的)集合,不再使用getter方法,而是使用 addFriendgetFriend(index) 等方法。

实际上,getter(尤其是setter)不再是一个很受欢迎的模式。

在我看来,成员变量前缀 "m" 更适合其他语言。

英文:

You are right for mutable objects. You could wrap the field with Collections.unmodifiableList and such. What one also sees, is for (mutable) collections just have no getters, but addFriend, getFriend(index) and such.

In fact getters (and estpecially setters) are no longer a very esteemed pattern.

The member "m" prefix is imho better suited for other languages.

huangapple
  • 本文由 发表于 2020年7月31日 18:25:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/63190090.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定