提问人:pgman 提问时间:1/8/2019 最后编辑:pgman 更新时间:6/12/2023 访问量:21905
Sonar - 存储副本 - 不应直接存储或返回可变成员
Sonar - Store a copy - Mutable members should not be stored or returned directly
问:
我有一个列表,是我班上的私人成员。 我使用 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)的答案都有效。我不能将两者都标记为接受的答案。所以在这里有一个注释
答:
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
值得注意的是,类也必须是不可变的。否则,其他一些类可以获取列表并更改其中的对象。中的对象也会发生变化。DEF
abc.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 添加额外的限制(不变性)。例如,如果您这样做,使用您的客户将无法对其进行排序。List
List
因此,我推荐的方法是:
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
在您的代码中,您是否初始化了字段,例如还是只有?defList
private 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<>());
}
}
评论
this.defList.addAll(defList);
defList