保护ArrayList不受写入权限的限制
考虑以下课程:
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
的克隆添加某些carList
,不会被告知这不是它意味着完成的方式。
您可以返回一个不可修改的视图,将返回类型更改为List<String>
而不是ArrayList<String>
:
public List<String> getCars() {
return Collections.unmodifiableList(carList);
}
请注意,由于Collections.unmodifiableList
只提供一个视图,调用者仍然会看到通过addToCarList
和removeFromCarList
(可能会重命名为addCar
和removeCar
)所做的任何其他更改。 那是你要的吗?
对返回视图的任何变异操作都将导致UnsupportedOperationException
。
首先,总是避免在赋值左侧使用具体类并作为方法的返回值。 所以,请将你的课程修复为
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
。 这将增加代码的封装级别,并使未来的修改更容易。
Jon Skeet的回答非常好(一如既往),但它没有涉及的一件事是并发问题。
如果多个线程同时访问这个对象,返回一个不可修改的集合仍然会带来问题。 例如,如果一个线程遍历汽车列表,然后同时另一个线程添加一张新卡。
您仍然需要以某种方式同步对该列表的访问,这也是为什么您可能会考虑返回列表的clone()
以及or而不是将其包装在unmodifiableList
的List封装器中的原因之一。 您仍然需要围绕clone()
同步,但是一旦克隆完成并且列表返回到查询代码,就不再需要同步。