Java: никаких изменений ArrayList из внешних классов

Я использую ArrayList в одном из классов моего проекта Java. Класс отслеживает, был ли изменен список, и предлагает общедоступные методы для добавления и удаления элементов из списка, которые автоматически устанавливают переменную как измененную.

Пока список является общедоступным, потому что я хочу, чтобы мой список был доступен для чтения отовсюду. Но я хочу, чтобы класс, которому принадлежит список, мог его изменить. Так что никаких модификаций извне классов. Это возможно? Если да, то как?

Обычно для управления доступом вы, вероятно, используете методы получения и установки. Но даже с методом получения и списком, установленным как частный, другой класс все равно может выполнить getList().remove(element) и, таким образом, изменить список извне, так что класс не заметит, что список был изменен.


person CGFoX    schedule 24.12.2014    source источник
comment
Обеспечьте всю желаемую функциональность только через публичные функции, полностью инкапсулируя внутреннюю структуру данных. Поэтому напишите свою собственную функцию удаления.   -  person SBI    schedule 24.12.2014


Ответы (5)


Сделайте ваше поле ArrayList приватным, но пусть геттер возвращает Collections.unmodifiableList(list), что обеспечивает неизменяемое представление.

Это позволит внешнему коду рассматривать его как обычный список, используя циклы for each и так далее, но отключит операции модификации. Кроме того, немодифицируемый список возвращает представление за постоянное время.

Это буквально точный вариант использования, для которого он был разработан. Javadoc:

Этот метод позволяет модулям предоставлять пользователям доступ «только для чтения» к внутренним спискам. Операции запроса в возвращаемом списке "считываются" в указанный список, а попытки изменить возвращенный список напрямую или через его итератор приводят к исключению UnsupportedOperationException.

person Louis Wasserman    schedule 25.12.2014

Сделайте свой список закрытым и добавьте метод получения:

public List getList(){
    return new ArrayList(yourPrivateList);
}
person Tkachuk_Evgen    schedule 24.12.2014

Вы можете сделать член ArrayList закрытым и вместо геттера, который возвращает ArrayList, иметь геттер, который принимает индекс i и возвращает i-й элемент ArrayList.

public class Test
{
    private List<String> list = new ArrayList<String>();

    public getString (int i)
    {
        // you might want to add some validation of i here
        return list.get(i);
    }

}

getString позволяет пользователям вашего класса получать доступ к любому элементу списка без возможности его изменения.

Если вы хотите, чтобы ваши пользователи могли перебирать список без возможности его изменения, вы можете добавить метод getSize() для возврата размера списка (что позволит пользователям перебирать список, используя обычный цикл for), или ваш класс может реализовать Iterable (без поддержки операции удаления).

person Eran    schedule 24.12.2014

Здесь у вас есть несколько вариантов:

  • Геттер, который возвращает ArrayList, может клонировать перед возвратом объекта. Таким образом, даже если внешний объект изменит объект, он в конечном итоге изменит клон, а не ваш исходный объект. Примечание. Операция клонирования может быть дорогостоящей. Я бы предложил вариант ниже.
  • Используйте 1_. Проверить документация здесь.
  • Или, как предлагают другие ответы: разверните свои собственные методы доступа и итерации.
person UltraInstinct    schedule 24.12.2014

Я думаю, что ваш лучший вариант здесь - сохранить ваш List private и добавить метод получения, который возвращает копию List, но не самого List. Например:

public class EncapsulationTest {

    private List<Object> privateList = new ArrayList<Object>();

    // Your constructors and methods to track list
    // modification here ...

    public List<Object> getList() {
        // Maybe you need a null check here
        return new ArrayList<Object>(privateList);
    }

    public void addElement(Object newElement) {
        this.privateList.add(newElement);
        // Set your 'changed' variable to true
    }

    public void removeElement(Object element) {
        this.privateList.remove(element);
        // Set your 'changed' variable to true
    }
}

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

Я надеюсь, что это помогает.

person Alvaro Vazquez    schedule 24.12.2014
comment
Thrustmaster упомянул в своем ответе, что возврат клона обходится дорого. Я использую список в эвристике, где время выполнения имеет первостепенное значение, а итерации по этим (большим) спискам происходят часто. - person CGFoX; 24.12.2014