英文:
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<String> mFriends;
// ...other code like constructors and setters...
public ArrayList<String> 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
- Java 不实现 按引用传递 机制,它一直是 按值传递;
- 你描述的问题被称为 Reference Escape,是的,你说得对,调用者可以通过 引用 修改你的对象;
- 为了避免 Reference Escape 问题,你可以选择:
- 返回对象的深拷贝(使用
.clone()
方法); - 使用现有数据创建一个新对象(例如
new ArrayList<>(yourObjectHere)
); - 或者想出其他方法,还有其他方式可以做到这一点;
- 返回对象的深拷贝(使用
- 这并不真正破坏封装,本质上 来说,这更多是关于正确的设计如何实现封装;
- 你对性能的担忧:不,这不会影响性能,而且与性能无关;相反,这涉及到面向对象编程的 正确设计,要么是对象的 可变性,要么是 不可变性。如果你总是返回深拷贝而不是引用,那么你就无法很好地掌握你的对象。
以你的例子为例:如果你想要更改对象的状态,而不只是通过其 setter 设置一个新对象,该怎么办?如果你想要修改现有的朋友(在你的例子中是这样的),你认为创建一个新的朋友列表并将其设置到对象中会更好吗?不,后一种情况下,你只是失去了对你的对象的控制。
英文:
- Java is not, and has never been implementing Pass by Reference mechanism, it has always been Pass by Value;
- 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;
- In order to avoid the Reference Escape problem, you can either:
- return a deep copy of the object (with
.clone()
); - create a new object with the existing data (e.g.
new ArrayList<>(yourObjectHere)
); - or come up with some other idea, there are some other ways too do this;
- return a deep copy of the object (with
- This does not really break the Encapsulation, per se, it is rather a point of correct design how you implement the encapsulation;
- 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<String> getFriends() {
return new ArrayList<>(mFriends);
}
By the way, Java is not truly pass-by-reference it's more pass-by-value.
答案3
得分: 1
你说的对于可变对象是正确的。你可以使用 Collections.unmodifiableList
来包装字段等。还有一种常见的做法是对于(可变的)集合,不再使用getter方法,而是使用 addFriend
、getFriend(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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论