考虑以下课程:
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;
}
}
如您所见,每次更改carList时,我都要通知Observers
。
如果有人getCarList().add(...);
,则会被规避。
如何为carList
提供读取权限(用于迭代等等),但是除了特殊方法addToCarList
和removeFromCarList
之外,阻止对它的写访问权限?
我想到了这个:
public ArrayList<String> getCarList() {
return (ArrayList<String>)carList.clone();
}
但有人使用我的课程,在向carList
的克隆添加内容时,不会被告知这不是它的意图。
答案 0 :(得分:40)
您可以返回其不可修改的视图,将返回类型更改为List<String>
而不是ArrayList<String>
:
public List<String> getCars() {
return Collections.unmodifiableList(carList);
}
请注意,由于Collections.unmodifiableList
仅提供视图,调用者仍会看到通过addToCarList
和removeFromCarList
进行的任何其他更改(我和#39; d可能重命名为addCar
和removeCar
。这就是你想要的吗?
对返回的视图进行任何变更操作都会产生UnsupportedOperationException
。
答案 1 :(得分:13)
首先,始终避免在赋值左侧使用具体类,并作为方法的返回值。所以,修改你的课程
public class Cars extends Observable{
private List<String> carList = new ArrayList<String>();
........................
public List<String> getCarList() {
return carList;
}
}
现在,您可以使用Collections.unmodifiableList()
将列表设为只读:
public List<String> getCarList() {
return Collections.unmodifiableList(carList);
}
顺便说一句,如果你真的不必返回List
,你可以返回Collection
甚至Iterable
。这将增加代码的封装级别,并使未来的修改更容易。
答案 2 :(得分:9)
Jon Skeet的回答非常好(一如既往),但它没有触及的一件事是并发问题。
如果多个线程同时访问此对象,则返回不可修改的集合仍会给您带来问题。例如,如果一个线程正在迭代汽车列表,然后同时另一个线程添加一张新卡。
您仍然需要以某种方式同步对该列表的访问,这就是为什么您可以考虑返回列表的clone()
以及将其包含在unmodifiableList
中的原因之一包装。您仍然需要在clone()
周围进行同步,但是一旦克隆完成并且列表返回到查询代码,它就不再需要同步了。
答案 3 :(得分:2)
我认为你可能会让你的Object实现Collection-Interface,如果它实际上是一个ObservableList。它是一个List,它应该是Observable - 所以它应该实现两个接口。
你甚至可以扩展名单&lt; ..&gt;因为你只想在当前功能中添加额外的功能(观察者),你的List可以在任何可以使用普通List的地方使用......
答案 4 :(得分:0)
使用Collections.unmodifiableList(list),因为它提供了一个无法修改的新List对象,在尝试更新/添加/删除对象列表时会抛出UnsupportedOperationException。