Sonar - 存储副本 - 不应直接存储或返回可变成员

Sonar - Store a copy - Mutable members should not be stored or returned directly

提问人:pgman 提问时间:1/8/2019 最后编辑:pgman 更新时间:6/12/2023 访问量:21905

问:

我有一个列表,是我班上的私人成员。 我使用 getter 和 setter 来获取和设置值。 SOnar 抛出错误 - 不应直接存储或返回可变成员。

例如:ABC 和 DEF 是两个类。

class ABC{
private List<DEF> defList;
public List<DEF> getDefList() { return defList; }
public void setDefList(List<DEF> defList) { this.defList = defList; }

经过大量的谷歌搜索和搜索,我了解到 getter 可以更改如下:

public List<DEF> getDefList() { return new ArrayList<>(defList); }

当我尝试类似地使用 setter 时,

public void setDefList(List<DEF> defList) { this.defList.addAll(defList); }

然后变量开始显示

'private field 'defList' is never assigned.

我可以知道当它是一个列表(另一个类的列表)时的正确方法吗?

注意:普拉萨德·卡鲁纳戈达(Prasad Karunagoda)和利奥·阿苏(Leo Aso)的答案都有效。我不能将两者都标记为接受的答案。所以在这里有一个注释

爪哇岛 数组列表 声纳库 可变

评论

0赞 Amongalen 1/8/2019
如果这样做,则为 null。在向其添加元素之前,您需要创建一个列表。this.defList.addAll(defList);defList
0赞 pgman 1/8/2019
但是,当它很简单时,它是如何工作的 this.defList = defList?

答:

9赞 Leo Aso 1/8/2019 #1

发出警告是因为您没有为字段提供初始值。这是您应该如何实现代码以确保不可变性 使用 .java.util.Collections

class ABC {
    private List<DEF> defList = Collections.emptyList();

    public List<DEF> getDefList() { 
        return defList;
    }

    public void setDefList(List<DEF> defList) {
        // defensively copy, then make immutable
        defList = new ArrayList<>(defList);
        this.defList = Collections.unmodifiableList(defList);
    }

评论

0赞 pgman 1/8/2019
感谢您的快速回复。那么在 getter 中它应该是 return Collections.unmodifiableList(defList) ??
0赞 Amongalen 1/8/2019
值得注意的是,类也必须是不可变的。否则,其他一些类可以获取列表并更改其中的对象。中的对象也会发生变化。DEFabc.defList
0赞 Leo Aso 1/8/2019
@pgman,您不需要在 getter 中再次将字段包装成 an,因为您已经在 setter 中使字段本身不可变。unmodifiableList
0赞 pgman 1/10/2019
如果是 Date,那么如何解决“不应直接存储或返回可变成员”错误?
1赞 Leo Aso 1/10/2019
Date 是可变的(并且已弃用),但在 Java 8 中,有一个包含不可变类的包,如 LocalDateTime 和 ZonedDateTime。您可以在 Google 上查找它以了解如何使用它。java.time
1赞 Prasad Karunagoda 1/8/2019 #2

我认为最好不要向从 getter 返回的 Return 添加额外的限制(不变性)。例如,如果您这样做,使用您的客户将无法对其进行排序。ListList

因此,我推荐的方法是:

public class ABC {
  private List<DEF> defList = new ArrayList<>();

  public List<DEF> getDefList() {
    return new ArrayList<>(defList);
  }

  public void setDefList(List<DEF> defList) {
    if (defList == null)
        throw new IllegalArgumentException("Parameter defList is null");
    this.defList.clear();
    this.defList.addAll(defList);
  }
}

从设计的角度来看,一个更好的类 API 是:ABC

public List<DEF> getDefList()
public void clearDefList()
public void addAllDefs(List<DEF> defs) // Or method name appendDefs

评论

0赞 pgman 1/8/2019
我已经使用了相同的方法,但问题是当我这样使用它时,它显示从未分配过“私有字段 defList”
0赞 Prasad Karunagoda 1/8/2019
在您的代码中,您是否初始化了字段,例如还是只有?defListprivate List<DEF> defList = new ArrayList<>();private List<DEF> defList;
0赞 pgman 1/10/2019
哦,我的错误。不好意思。是的,我没有使用 = new ArrayList<>()
0赞 pgman 1/10/2019
一个澄清......this.defList.clear() 在这里重要吗?
1赞 Clement Cherlin 10/22/2021
@PrasadKarunagoda “例如,如果您这样做,使用您的列表的客户将无法对其进行排序。”就是这个想法。一般来说,客户端不应该直接修改其他对象的私有字段。想象一下,如果不同的客户端在多个线程中运行!想要排序列表的客户需要制作副本。
0赞 Bruno Martins 6/12/2023 #3
public class ABC {
    private List<DEF> defList = Collections.emptyList();

    public List<DEF> getDefList() { 
        List<DEF> defListCopy = new ArrayList();
        if(defList != null){
         defListCopy = Collections.unmodifiableList(defList);
        }
        return defListCopy;
    }

    public void setDefList(final List<DEF> defList) {
        this.defList = Optional.ofNullable(defList).orElse(new ArrayList<>());
    }
}

评论

1赞 alea 6/12/2023
您的答案可以通过其他支持信息进行改进。请编辑以添加更多详细信息,例如引文或文档,以便其他人可以确认您的答案是正确的。您可以在帮助中心找到有关如何写出好答案的更多信息。