我有一个getter,它返回一个不可修改的列表,例如:
public List<Product> getProductList() {
if (productList == null)
return new ArrayList<>();
return Collections.unmodifiableList(productList);
}
我这样称呼这个吸气剂:
List<Product> productList = new ArrayList<>(report.getProductList());
然后我将此列表传递给另一个修改列表的方法,
for (Product product : productList) {
product.addToAdvisoryList(advisory);
}
其中addToAdvisoryList(咨询顾问)为:
public void addToAdvisoryList(Advisory advisory) {
if (advisoryList == null) {
setAdvisoryList(Collections.singletonList(advisory));
} else if (!isContainedAdvisory(advisoryList, advisory)) {
List<Advisory> newAdvisoryList = new ArrayList<>(advisoryList);
newAdvisoryList.add(advisory);
setAdvisoryList(newAdvisoryList);
}
}
运行这些代码后,原始产品列表将被修改。 有人可以解释到底发生了什么吗?以及如何避免修改不可修改的列表?
答案 0 :(得分:0)
在您提供的代码中,原始(不可修改)列表作为参数传递给java.util.ArrayList
(可修改列表)的构造函数。
List<Advisory> newAdvisoryList = new ArrayList<>(advisoryList);
此构造函数使用其迭代器返回的顺序(请参见documentation),以提供的Collection
的所有项目创建一个新列表。
同样的事情发生在产品列表上:
List<Product> productList = new ArrayList<>(report.getProductList());
这使得首先返回不可修改的列表变得多余。
换句话说,您不是在修改不可修改的列表。您将使用不可修改(不可变)列表中的元素创建可修改的新列表,然后修改(可变)列表。
有关不可变列表的信息,请参见Java文档中的here。尝试修改不可变列表时,您会得到UnsupportedOperationException
。
编辑
回答有关吸气剂的问题:
“获取器的目的不是返回对象的只读副本吗?”
getter用于从外部提供对私有或受保护的类成员的访问。
如果您有一个类似的课程(在您的示例中)Report
public class Report {
private List<Product> products;
public void setProducts(List<Product> products) {
this.products = products;
}
public List<Product> getProducts() {
return products;
}
}
getProducts
使列表可以从类外部访问(因此,如果可变,则可以添加或删除元素)。尽管有争议,但通常使用Java bean来完成这种事情(考虑使用不需要不必要地公开状态信息的方法来代替getter和setter)。无论如何,请参见here,以获取有关根据Oracle的getter和setter的更多信息。
答案 1 :(得分:0)
您的列表包含可变产品对象。
您不修改任何列表。列表包含对相同可变对象的引用,您正在对这些对象进行更改。基本上是与
相同的旧问题为避免这种情况,您可以使getProductList
返回份的列表。另外,您可以使Product类不可变,这将迫使您重新考虑您的方法。
public List<Product> getProductList() {
List<Product> copies = new ArrayList<>();
for (Product product: productList)
copies.add(new Product(product)); // add this constructor
return copies;
}