Подтвердить что ты не робот

Защитить 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 не должен быть проинформирован о том, что это не так, как это должно было быть сделано.

4b9b3361

Ответ 1

Вы можете вернуть немодифицируемое представление об этом, изменив тип возврата на List<String> вместо ArrayList<String>:

public List<String> getCars() {
    return Collections.unmodifiableList(carList);
}

Обратите внимание, что поскольку Collections.unmodifiableList предоставляет только представление, вызывающий абонент все равно увидит любые другие изменения, сделанные с помощью addToCarList и removeFromCarList (который я бы переименовал в addCar и removeCar, возможно). Это то, что вы хотите?

Любые мутирующие операции на возвращенном представлении приведут к UnsupportedOperationException.

Ответ 2

Во-первых, всегда избегайте использования конкретного класса в левой части назначения и в качестве возвращаемого значения метода. Итак, исправьте свой класс как

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. Это увеличит уровень инкапсуляции вашего кода и облегчит будущие изменения.

Ответ 3

Ответ на Jon Skeet превосходный (как всегда), но единственное, на что он не касается, - это проблемы concurrency.

Возвращение немодифицируемой коллекции все равно оставит вас с проблемами, если несколько потоков обращаются к этому объекту в одно и то же время. Например, если один поток выполняет итерацию по списку автомобилей, и в то же время другой поток добавляет новую карту.

Вам все равно нужно синхронизировать доступ к этому списку, и это одна из причин, по которой вы можете рассмотреть возможность возврата clone() списка, а также вместо того, чтобы просто обернуть его в обертку unmodifiableList. Вам все равно нужно будет синхронизировать вокруг clone(), но как только клон будет завершен, и список вернется в код запроса, его больше не нужно синхронизировать.

Ответ 4

Я думаю, вы, вероятно, могли бы сделать свой Object реализацией Collection-Interface, если это фактически ObservableList. Это список, и он должен быть Observable - поэтому он должен реализовывать оба интерфейса.

Вы можете даже расширить список <.. > , потому что просто хотите добавить дополнительную функциональность (наблюдатели) к текущим функциям, и ваш список можно использовать везде, где может использоваться обычный список...

Ответ 5

используйте Collections.unmodifiableList(list), поскольку он предоставляет новый объект List, который не может быть изменен, он будет генерировать исключение UnsupportedOperationException при попытке обновить/добавить/удалить список объектов.