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;
}