简体   繁体   English

为什么局部非最终变量会影响引用字段? 爪哇

[英]Why local non-final variable affects the referenced field? Java

I have following code in my program: 我的程序中有以下代码:

...
private void generateStack() {
    List<AdvertisementsModel> adsForModel = Storage.getAdsForId(model.getId());
    ...
    adsForModel.clear();
...

Storage is static class with static fields and methods. Storage是具有静态字段和方法的静态类。 generateStack method is in another class and in instance object. generateStack方法在另一个类和实例对象中。 Why does adsForModel.clear(); 为什么adsForModel.clear(); affects the list in Storage class if asdForModel reference is not final? 如果asdForModel引用不是最终的,会影响Storage类中的列表吗?

Storage.getAdsForId(...) returns a copy of the reference to the same List object. Storage.getAdsForId(...)返回对同一List对象的引用的副本。 So calls via this reference effect the same list. 因此,通过该引用进行的调用会影响相同的列表。 When you call Storage.getAdsForId there is no new List created - just a new reference to the same list. 调用Storage.getAdsForId ,不会创建新的列表-只是对同一列表的新引用。

Therefore it's good practise to either return explicitly ImmutableList or making a defensive copy of the list in Storage.getAdsForId(...) and returning the copy. 因此,优良作法是显式返回ImmutableList或在Storage.getAdsForId(...)列表的防御性副本并返回该副本。

Be aware that you need to make a deep copy when AdvertisementsModel is mutable or you'll run into the same problem on a different level. 请注意,当AdvertisementsModel可变时,您需要进行深层复制,否则您将在不同级别遇到相同的问题。 (Otherwise you may have a list copy now but both lists still containing references to the same AdvertisementsModel objects and changing them still effect the list contents inside Storage .) (否则,您现在可能已有一个列表副本,但是两个列表仍然包含对相同AdvertisementsModel对象的引用,并且更改它们仍然会影响Storage的列表内容。)

Java is pass by value (of the reference). Java是按值传递(引用的)。 So, if Storage.getAdsForId(model.getId()) returns a reference which is staticly stored within Storage then calling clear() on it in the instance will affect the same List within Storage as well. 因此,如果Storage.getAdsForId(model.getId())返回静态存储在Storage中的引用,则在实例中对其调用clear()也会影响Storage的同一List。 You could do this instead: 您可以改为:

return new ArrayList<AdvertisementsModel>(Storage.getAdsForId(model.getId()));

to return a copy of the list instead which would avoid affecting the list within Storage. 而是返回列表的副本,这样可以避免影响Storage中的列表。 Of course, modifying an element of this list will still affect the same element present within the list in Storage . 当然,修改此列表的元素仍然会影响Storage列表中存在的相同元素。 To avoid that, you'd have to deep copy each element in the list. 为了避免这种情况,您必须深度复制列表中的每个元素。

getAdsForId应该返回列表的副本,否则它将返回引用,并且在列表上调用clear将清空原始列表。

If it is final, is the original list not affected? 如果是最终版本,原始列表是否不受影响? I have doubts about that... It's the same list instance. 我对此表示怀疑...这是相同的列表实例。 For this, I'd use either 为此,我将使用

new ArrayList<AdvertisementsModel>(Storage.getAdsForId(model.getId()));

which creates a new list instance and if possible, modified the Storage class to return an UnmofifiableList of the original list: 它创建一个新的列表实例,并在可能的情况下修改Storage类以返回原始列表的UnmofifiableList:

return Collections.unmodifiableList(adsForIdList);

I'd prefer this, as this solution does not create a new List instance with each call, it is the responsibility of the receiving code to decide if that needs to be created or not. 我更喜欢这样做,因为此解决方案不会在每次调用时都创建一个新的List实例,因此接收代码的责任是确定是否需要创建该实例。 However, in multithreaded environments, if the original list might be modified, this might result in ConcurrentModificationException s - so in that case it is wiser to create a "defensive copy" of the list in the getter itself. 但是,在多线程环境中,如果原始列表可能被修改,则可能会导致ConcurrentModificationException ,因此在这种情况下,在吸气剂本身中创建列表的“防御性副本”是更明智的选择。 Be sure to keep in mind, that then the modifications to the copy will not affect the original list... 请切记,对副本的修改不会影响原始列表...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM