Collections.unmodifiableList (Java 8 SE) 在从中创建新的 ArrayList 时如何修改?
How does Collections.unmodifiableList (Java 8 SE) get modified when making a new ArrayList from it?
我有一个 getter returns 一个不可修改的列表,因此:
public List<Product> getProductList() {
if (productList == null)
return new ArrayList<>();
return Collections.unmodifiableList(productList);
}
我这样称呼 getter:
List<Product> productList = new ArrayList<>(report.getProductList());
然后我将这个列表传递给另一个修改列表的方法:
for (Product product : productList) {
product.addToAdvisoryList(advisory);
}
其中 addToAdvisoryList(Advisory advisory) 是:
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);
}
}
在运行这些代码之后,修改了原来的产品列表。
有人可以解释到底发生了什么吗?以及如何避免修改不可修改的列表?
在您提供的代码中,原始(不可修改的)列表作为参数传递给 java.util.ArrayList
可修改列表的构造函数。
List<Advisory> newAdvisoryList = new ArrayList<>(advisoryList);
此构造函数创建一个新列表,其中包含所提供的 Collection
的所有项目,其顺序由其迭代器 return 编辑(参见 documentation)。
同样的事情发生在产品列表中:
List<Product> productList = new ArrayList<>(report.getProductList());
这首先使它对于 return 不可修改的列表来说是多余的。
换句话说,您不是在修改不可修改的列表。您正在使用不可修改(不可变)列表中的元素创建可修改的新列表,然后修改(可变)列表。
另见 here 不可变列表,来自 Java 文档。尝试修改不可变列表时,您将得到 UnsupportedOperationException
。
编辑
回答关于 getters 的问题:
"Isn't the purpose of a getter to return a read-only copy of the object?"
getter 用于从外部提供对私有或受保护 class 成员的访问。
如果你有一个 class 喜欢(在你的例子中)Report
public class Report {
private List<Product> products;
public void setProducts(List<Product> products) {
this.products = products;
}
public List<Product> getProducts() {
return products;
}
}
getProducts
使列表可以从 class 外部访问(因此您可以添加或删除可变元素)。这种事情通常是用 Java bean 完成的,尽管它的使用存在争议(考虑用不会不必要地暴露状态信息的方法替换 getters 和 setter)。在任何情况下,请参阅 here 了解有关 getters 和 setter 根据 Oracle 的更多信息。
您的列表包含可变 个产品对象。
您没有修改任何列表。这些列表包含对相同可变对象的引用,而您正在对这些对象进行更改。这基本上是与
相同的老问题
为避免这种情况,您可以制作 getProductList
return 份 的列表。或者,您可以使产品 class 不可变,这将迫使您重新考虑您的方法。
public List<Product> getProductList() {
List<Product> copies = new ArrayList<>();
for (Product product: productList)
copies.add(new Product(product)); // add this constructor
return copies;
}
我有一个 getter returns 一个不可修改的列表,因此:
public List<Product> getProductList() {
if (productList == null)
return new ArrayList<>();
return Collections.unmodifiableList(productList);
}
我这样称呼 getter:
List<Product> productList = new ArrayList<>(report.getProductList());
然后我将这个列表传递给另一个修改列表的方法:
for (Product product : productList) {
product.addToAdvisoryList(advisory);
}
其中 addToAdvisoryList(Advisory advisory) 是:
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);
}
}
在运行这些代码之后,修改了原来的产品列表。 有人可以解释到底发生了什么吗?以及如何避免修改不可修改的列表?
在您提供的代码中,原始(不可修改的)列表作为参数传递给 java.util.ArrayList
可修改列表的构造函数。
List<Advisory> newAdvisoryList = new ArrayList<>(advisoryList);
此构造函数创建一个新列表,其中包含所提供的 Collection
的所有项目,其顺序由其迭代器 return 编辑(参见 documentation)。
同样的事情发生在产品列表中:
List<Product> productList = new ArrayList<>(report.getProductList());
这首先使它对于 return 不可修改的列表来说是多余的。
换句话说,您不是在修改不可修改的列表。您正在使用不可修改(不可变)列表中的元素创建可修改的新列表,然后修改(可变)列表。
另见 here 不可变列表,来自 Java 文档。尝试修改不可变列表时,您将得到 UnsupportedOperationException
。
编辑
回答关于 getters 的问题:
"Isn't the purpose of a getter to return a read-only copy of the object?"
getter 用于从外部提供对私有或受保护 class 成员的访问。
如果你有一个 class 喜欢(在你的例子中)Report
public class Report {
private List<Product> products;
public void setProducts(List<Product> products) {
this.products = products;
}
public List<Product> getProducts() {
return products;
}
}
getProducts
使列表可以从 class 外部访问(因此您可以添加或删除可变元素)。这种事情通常是用 Java bean 完成的,尽管它的使用存在争议(考虑用不会不必要地暴露状态信息的方法替换 getters 和 setter)。在任何情况下,请参阅 here 了解有关 getters 和 setter 根据 Oracle 的更多信息。
您的列表包含可变 个产品对象。
您没有修改任何列表。这些列表包含对相同可变对象的引用,而您正在对这些对象进行更改。这基本上是与
相同的老问题为避免这种情况,您可以制作 getProductList
return 份 的列表。或者,您可以使产品 class 不可变,这将迫使您重新考虑您的方法。
public List<Product> getProductList() {
List<Product> copies = new ArrayList<>();
for (Product product: productList)
copies.add(new Product(product)); // add this constructor
return copies;
}