我的程序中有以下代码:
...
private void generateStack() {
List<AdvertisementsModel> adsForModel = Storage.getAdsForId(model.getId());
...
adsForModel.clear();
...
Storage
是具有静态字段和方法的静态类。 generateStack
方法在另一个类和实例对象中。如果adsForModel.clear();
引用不是最终的,那么为什么Storage
会影响asdForModel
类中的列表?
答案 0 :(得分:3)
Storage.getAdsForId(...)
返回对同一List
个对象的引用副本。因此,通过此引用调用会调用相同的列表。当您致电Storage.getAdsForId
时,没有创建新的列表 - 只是对同一列表的新引用。
因此,最好明确返回ImmutableList
或在Storage.getAdsForId(...)
中制作列表的防御副本并返回副本。
请注意,当AdvertisementsModel
可变时您需要制作一份深层副本,否则您将在不同级别遇到同样的问题。 (否则,您现在可能有一个列表副本,但两个列表仍包含对相同AdvertisementsModel
个对象的引用,并且更改它们仍会影响Storage
中的列表内容。)
答案 1 :(得分:3)
Java是值(引用的)传递。因此,如果Storage.getAdsForId(model.getId())
返回一个静态存储在Storage中的引用,那么在实例中调用它clear()
将影响Storage
中的同一个List。你可以这样做:
return new ArrayList<AdvertisementsModel>(Storage.getAdsForId(model.getId()));
返回列表的副本,这样可以避免影响Storage中的列表。当然,修改此列表的元素仍将影响Storage
中列表中的相同元素。为避免这种情况,您必须深度复制列表中的每个元素。
答案 2 :(得分:1)
getAdsForId
应该返回列表的副本,否则它将返回一个引用,并且在列表中调用clear
将清空原始列表。
答案 3 :(得分:1)
如果是最终的,原始列表是否不受影响?我对此有疑问......这是同一个列表实例。为此,我要使用
new ArrayList<AdvertisementsModel>(Storage.getAdsForId(model.getId()));
创建一个新的列表实例,如果可能,修改Storage
类以返回原始列表的UnmofifiableList:
return Collections.unmodifiableList(adsForIdList);
我更喜欢这个,因为这个解决方案不会在每次调用时创建一个新的List实例,接收代码负责决定是否需要创建。但是,在多线程环境中,如果原始列表可能被修改,这可能会导致ConcurrentModificationException
s - 所以在这种情况下,在getter本身中创建列表的“防御副本”是明智的。请务必记住,然后对副本的修改不会影响原始列表...