Protect ArrayList from write access
考虑以下类别:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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(...);,这是规避的。
除了特殊的方法addToCarList和removeFromCarList,我如何授予对carList的读访问权(用于对它进行迭代等),但阻止对它的写访问?
我想到了:
1 2 3
| public ArrayList<String> getCarList() {
return (ArrayList<String>)carList.clone();
} |
但是,当有人使用我的类向carList的克隆体添加某些内容时,他不会被告知这不是该做的事情。
- 尽管在我看来,从arraylist扩展,重写add和remove函数来通知观察者,并使observable成为一个接口似乎是一种更简单的处理方法。
- 与其提供getcarlist()方法,不如提供getcaratindex()和getcarcount()。
- @我会喜欢那个吗?甚至可以实现迭代器!实际上可能是个很好的答案。
您可以返回不可修改的视图,将返回类型更改为List,而不是ArrayList:
1 2 3
| public List <String > getCars () {
return Collections. unmodifiableList(carList );
} |
请注意,由于Collections.unmodifiableList只提供了一个视图,调用方仍将看到通过addToCarList和removeFromCarList所做的任何其他更改(我可能将其重命名为addCar和removeCar)。这就是你想要的吗?
返回视图上的任何变化操作都将导致UnsupportedOperationException。
- 这是正确的。只需确保你的卡尔斯特是私人的(即使它已经是)。如果不是这样,用户仍然有可能以这种方式访问变量。请参见:javacreed.com/modifying-an-unmodifiable-list
- @DBERM22:这已经是私人问题了……我不知道你为什么提起这个。
- 我知道……我只是在补充,这样未来的读者就看不到你的正确例子,然后忽略了公共变量而破坏了它。这只是对未来观众的一个警告,而不是对OP的一个提示。
首先,总是避免在赋值的左侧使用具体的类,并将其作为方法的返回值。所以,把你的课改成
1 2 3 4 5 6 7 8 9
| public class Cars extends Observable{
private List <String > carList = new ArrayList <String >();
........................
public List <String > getCarList () {
return carList ;
}
} |
现在您可以使用Collections.unmodifiableList()使您列出只读:
1 2 3
| public List <String > getCarList () {
return Collections. unmodifiableList(carList );
} |
顺便说一句,如果你真的不需要归还List,你可以归还Collection,甚至Iterable。这将提高代码的封装级别,并使以后的修改更加容易。
乔恩·斯基特的回答非常好(一如既往),但它没有涉及到的一件事是并发性问题。
如果多个线程同时访问这个对象,返回不可修改的集合仍然会给您带来问题。例如,如果一个线程正在遍历汽车列表,然后同时另一个线程添加一张新卡。
您仍然需要以某种方式同步对该列表的访问,这也是您可能考虑返回该列表的clone()的原因之一,也可以考虑将其包装在unmodifiableList包装中,而不只是将其包装在unmodifiableList包装中。您仍然需要在clone()周围进行同步,但是一旦克隆完成,并且列表返回到查询代码,就不再需要同步。
- 好点。乔恩·斯基特实际上提到,Collection.unmodifiable()返回一个视图,但从他的回答中不清楚它在多线程环境中会导致什么。
我认为,如果对象实际上是一个可观察列表,那么您可以让它实现集合接口。它是一个列表,应该是可观察的——所以它应该实现两个接口。
您甚至可以扩展列表<..>因为您只想向当前功能添加额外的功能(观察者),并且您的列表可以在任何可以使用普通列表的地方使用…
- 请参见:stackoverflow.com/questions/21692193/why not inherit from li&zwnj;&8203;stt/&hellip;
- 隐马尔可夫模型。。。我在这里看到一个完全不同的用例。实际上,他希望实现一个可观察的集合,这是一个可观察的集合。也就是说,应该实现收集接口。然后他可以像readonlylist类那样做,只对不支持的操作抛出异常,并通知侦听器等等……
- 对于一个通用的ObservableCollection来说,如果这就是他所说的有意义的话,这实际上取决于所讨论的类在做什么,但这是一个有效的观点。删除了投反对票:)
- C中的TIMB,一个列表是一个具体的类,但是在Java列表中是一个接口,所以我不认为这里的答案是相关的。
- @davidconrad这真的取决于他写的是一个通用的ObservableList,还是另一个包含ObservableList的类。尽管如此,第二种情况最好还是先创建ObservableList,然后在内部使用它,所以实际上我正在改变主意。
使用collections.unmodifiablelist(list)因为它提供了一个无法修改的新列表对象,所以在尝试更新/添加/删除对象列表时,它将引发一个unsupportedOperationException。