具有 ArrayList 成员变量的不可变对象 - 为什么可以更改该变量?
我有一个包含各种成员变量的类。有一个构造函数,有 getter 方法,但没有 setter 方法。事实上,这个对象应该是不可变的。
public class Example {
private ArrayList<String> list;
}
现在我注意到以下内容:当我使用 getter 方法获取变量列表时,我可以添加新值等等 - 我可以更改 ArrayList。当我下次调用此变量的 get()
时,将返回更改后的 ArrayList
。怎么会这样呢?我没有再设置,我只是努力而已!
对于String
,这种行为是不可能的。那么这里有什么区别呢?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
仅仅因为对列表的引用是不可变的,并不意味着它引用的列表是不可变的。
即使
list
被设为final
,这也是允许的,但这是不允许:
为了使列表不可变(也防止第一行),我建议您使用
Collections.unmodifyingList
:(请注意,这会创建列表的不可修改视图。如果有人保留原始引用,则仍然可以通过该视图修改列表。 )
这是因为
String
已经是不可变的(不可修改的),就像将列表变成不可修改的列表一样。比较:
Just because the reference to the list is immutable doesn't mean that the list it refers to is immutable.
Even if
list
was madefinal
this would be allowedbut this would not allowed:
In order to make the list immutable (prevent also the first line), I suggest you use
Collections.unmodifiableList
:(Note that this creates an unmodifiable view of the list. If someone is holding on to the original reference, then the list can still be modified through that.)
That is because a
String
is already immutable (unmodifiable) just as the list would be if you turned it into an unmodifiableList.Comparison:
您将返回对
列表
的引用。并且list
并不是一成不变的。如果您不希望更改列表,请返回其副本:
或者您可以返回不可修改的列表:
You are returning a reference to the
list
. Andlist
isn't immutable.If you do not want your list to be changed return a copy of it:
Or you can return a unmodifiable list:
关键是要明白您不是在更改字符串 - 您正在更改引用列表中包含的字符串。
换句话说:如果我从你的钱包里取出一美元,然后用一毛钱代替,那么我既没有改变这美元,也没有改变一毛钱——我只是改变了你钱包里的东西。
如果您想要列表的只读视图,请查看
Collections.unmodifyingList
。当然,这不会阻止它所包装的列表发生更改,但它会阻止任何仅引用不可修改列表的人修改内容。要获得真正的不可变列表,请查看 Guava 的 ImmutableList 类。
The key is to understand that you're not changing the string - you're changing which string references the list contains.
To put it another way: if I take a dollar out of your wallet, and replace it with a dime, I haven't changed either the dollar or the dime - I've just changed the contents of your wallet.
If you want a read-only view on the list, look at
Collections.unmodifiableList
. That won't stop the list that it's wrapping from changing of course, but it will stop anyone who only has a reference to the unmodifiable list from modifying the contents.For a truly immutable list, look at Guava's ImmutableList class.
正如其他答案所说,您从 getter 返回的对象仍然是可变的。
您可以通过使用 Collections 类修饰 List 将其变为不可变:
如果将其返回给客户端,他们将无法向其中添加或删除元素。然而,他们仍然可以从列表中获取元素 - 所以你必须确保它们也是不可变的,如果这就是你想要的!
As the other answers say the Object you return from the getters is still mutable.
You can turn the List immutable by decorating it using the Collections class:
If you return this to clients they will not be able to add or remove elements to it. However, they can still get elements out of the list - so you have to make sure they're immutable too, if that's what you're after!
Collections.unmodificList() 使列表不可修改。这再次创建一个新的最终数组列表并重写 add、remove、addall 和clear 方法以抛出不支持的操作异常。它是 Collections 类的 hack。但在编译时,它不会阻止您添加和删除内容。我宁愿克隆该列表。这可以帮助我保持现有对象不可变,并且不需要我创建新列表。阅读克隆和新运算符之间的区别(http://www.javatpoint.com/object-cloning )。也有助于防止我的代码在运行时崩溃。
Collections.unmodifiableList() makes list unmidifiable. Which again creates a new final array list and override add, remove, addall and clear method to throw unsupportedoperationexception. Its a hack of Collections class. But at compile time it does not stop you from adding and removing stuff. I would rather go with cloning of that list. Which can help me to keep my existing object immutable and does not cost me creating of new list. Read difference between cloning and new operator(http://www.javatpoint.com/object-cloning). Also will help from crashing my code at runtime.
这对我有用。我们需要将该类设置为final,这样它就不能被继承。保留该列表为最终列表(这只是对该列表的引用)。在构造函数中,使用 unmodifyingList 并创建一个新列表,并分配对该列表的引用而不是输入。
This works for me. We need to have the class as final so that it can not be inherited. keep the list as final (this is just a reference to the list). In the constructor, use unmodifiableList and create a new list and assign a reference to that and not the input.
从 Java 10 开始,您可以使用
List.copyOf
静态方法。根据 javadoc ,结果列表是不可变的,对基础列表的任何更改都不会影响新列表。
Since Java 10 you can use
List.copyOf
static method.As per javadoc, the resulting list is immutable, and any changes to the underlying list will not affect the new one.
因此,如果您想保护列表不被更改,则不应为列表提供 getter 方法。
由于没有设置器,它的对象仍然保持完整。但是你所做的就是向其中删除/添加新的/不同的对象,这很好。
Therefore you should not provide a getter method for the list if you want to protect it from being changed.
Its objects are still staying intact since you didn't have setters. But what you do is remove/add new/different objects to it and this is fine.
列表引用是不可变的,但列表不是。如果您希望列表本身不可变,请考虑使用 不可变列表
The list reference is immutable, but not the list. If you want the list itself to be immutable, consider using ImmutableList
要获得真正不可变的列表,您必须对列表的内容进行深层复制。 UnmodifierList 只会使引用列表在某种程度上不可变。
现在,随着大小的增加,对列表或数组进行深度复制将在内存上变得困难。
您可以利用序列化/反序列化并将数组/列表的深层副本存储到临时文件中。由于成员变量需要不可变,因此设置器将不可用。 getter 会将成员变量序列化到文件中,然后对其进行反序列化以获得深层副本。序列化具有深入对象树的本质。不过,这将确保完全的不变性,但会牺牲一些性能。
To get a really immutable list, you will have to make deep copies of the contents of the list. UnmodifiableList would only render the list of references somewhat immutable.
Now making a deep copy of the List or array will be tough on memory with the growing size.
You can make use of serialization/deserialization and store the deep copy of array/list into a temp file. The setter would not be available as the member varaible needs to be immutable. The getter would serialize the member variable into a file and then desialize it to get a deep copy. Seraialization has an innate nature of going into the depths of an object tree. This would ensure complete immutability at some performance cost though.
另一种解决方案是返回 arraylist 的副本,而不是像下面的代码那样返回实际的数组列表
One another solution is to return copy of the arraylist not the actual array list like this code