保护ArrayList不受写访问

asc*_*sco 29 java

考虑以下课程:

public class Cars extends Observable{

    private ArrayList<String> carList = new ArrayList<String>();

    public void addToCarList(String car){
        // ...
        hasChanged();
        notifyObservers();
    }

    public void removeFromCarList(String car){
        // ...
        hasChanged();
        notifyObservers();
    }

    public ArrayList<String> getCarList() {
        return carList;
    }    
}
Run Code Online (Sandbox Code Playgroud)

如您所见,每次更改carList时,我都要通知Observers.如果有人这样做getCarList().add(...);,这就被规避了.

我如何提供carList对它的读访问权(用于迭代它等),但是除了特殊方法addToCarListremoveFromCarList?之外,它阻止对它的写访问 ?

我想到了这个:

public ArrayList<String> getCarList() {
    return (ArrayList<String>)carList.clone();
}
Run Code Online (Sandbox Code Playgroud)

但是有人使用我的课程,在向克隆添加一些内容时carList,不会被告知这不是它的意图.

Jon*_*eet 41

您可以返回不可修改的视图,将返回类型更改List<String>ArrayList<String>:

public List<String> getCars() {
    return Collections.unmodifiableList(carList);
}
Run Code Online (Sandbox Code Playgroud)

注意,只是提供了一个视图,调用者仍然会看到通过其他更改和(我会重命名为和,可能).那是你要的吗?Collections.unmodifiableList addToCarListremoveFromCarListaddCarremoveCar

对返回的视图的任何变异操作都将导致UnsupportedOperationException.


Ale*_*exR 13

首先,始终避免在赋值的左侧使用具体类,并作为方法的返回值.所以,修改你的课程

public class Cars extends Observable{

    private List<String> carList = new ArrayList<String>();
    ........................

   public List<String> getCarList() {
        return carList;
   }
}    
Run Code Online (Sandbox Code Playgroud)

现在您可以使用Collections.unmodifiableList()以使列表只读:

   public List<String> getCarList() {
        return Collections.unmodifiableList(carList);
   }
Run Code Online (Sandbox Code Playgroud)

顺便说一句,如果你真的没有回来,List你可能会回来Collection甚至Iterable.这将增加代码的封装级别,并使未来的修改更容易.


Tim*_*m B 9

Jon Skeet的答案很好(一如既往),但它没有触及的一件事是并发问题.

如果多个线程同时访问此对象,则返回不可修改的集合仍会给您带来问题.例如,如果一个线程正在迭代汽车列表,然后同时另一个线程添加一个新卡.

您仍然需要以某种方式同步对该列表的访问,这就是为什么您可以考虑返回clone()列表的一个原因,或者只是将其包装在unmodifiableList包装器中的原因之一.您仍然需要围绕它进行同步,clone()但是一旦完成克隆并且列表返回到查询代码,就不再需要同步它.