Почему getter & setter, если возвращаемое значение является изменяемым?

В С++ getter и setter для частного элемента данных очень полезны из-за способности управлять изменчивостью с помощью возвращаемого значения const.

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

Если это так (и, пожалуйста, поправьте меня, если у меня есть грубое неправильное представление здесь), почему бы не объявить элемент данных public и упростить вещи?

Ответ 1

Выполнение immutable возвращаемых значений в java - это вопрос о возврате уже типов объектов immutable (таких как String) или возвращая копию для неизменяемых объектов.


Пример 1 - уже неизменный объект

public String getValue() {
    return value;
}

Пример 2 - сбор уже неизменяемых объектов

public List<String> getValues() {
    return new ArrayList<String>(values);
}

Пример 3 - объект, не подлежащий неизменному

public Complex getComplex() {
    return complex.clone();
}

Образец 4 - сбор объектов, не подлежащих неизменному

public List<Complex> getComplex() {
    List<Complex> copy = new ArrayList<Complex>(complexs.size());
    for (Complex c : complexs) 
        copy.add(c.clone());
    return copy;
}

Примеры 3 и 4 предназначены для удобства на основе того, что сложный тип реализует интерфейс Cloneable.

Кроме того, чтобы избежать подклассов, переопределяющих ваши неизменные методы, вы можете объявить их final. В качестве дополнительной заметки шаблон builder обычно полезен для построения неизменяемых объектов.

Ответ 2

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

  • обернуть коллекции с неизменяемыми декораторами или защитить их до возвращения из геттера
  • сделайте копию Date и Calendar
  • Возвращать неизменяемые объекты или защищать их clone. Это также относится к объектам в коллекциях.

Обратите внимание, что если вы защищаете коллекцию, клиент может просмотреть или изменить копию, но это не влияет на исходную коллекцию:

return new ArrayList<Foo>(foos);

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

return Collections.unmodifiableList(foos);

Нижняя строка: Foo также должна быть неизменной, иначе коллекция будет неизменной, но код клиента все еще может изменять членов коллекции. Таким образом, те же правила применяются к Foo.

Если это так (и, пожалуйста, поправьте меня, если у меня есть грубое неправильное представление здесь), почему бы не объявить пользователя данных публичным и упростить?

Потому что:

  • вы можете захотеть сохранить изменяемые данные внутри объекта и предоставить только неизменяемое (доступное только для чтения) представление данных (например, коллекций оберток).
  • вы можете изменить реализацию в будущем, избавиться от поля и, например, вычислить значение "на лету".

Ответ 3

Если вы хотите вернуть неизменяемое представление изменяемого стандартного контейнера (например, списка), то вы должны взглянуть на библиотеку Collections:

http://download.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html

Он предоставляет некоторые полезные обертки, такие как unmodifiableMap и unmodifiableList. Таким образом, вам не нужно делать расточительную копию. Конечно, если элементы списка изменяемы, то это не поможет, - нет простого способа в Java получить "глубокую" неизменность. Конечно, то же самое верно и в С++ - например, если у вас есть вектор-указатель указателей на объекты Foo, то сами объекты Foo все равно могут быть изменены (поскольку const не распространяется по указателям).

Ответ 4

Если это так (и, пожалуйста, поправьте меня, если у меня есть грубое неправильное представление здесь), почему бы не объявить пользователя данных публичным и упростить?

Прежде всего, спецификация JavaBeans. требует, чтобы вы предоставляли геттеры (и сеттеры для изменяемых свойств).

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

Ответ 5

почему бы не объявить пользователя данных публичным и упростить?

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

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

Чтобы уточнить, вызывающий абонент не может изменить элемент данных, возвращенный с помощью геттера. Он может изменить объект, к которому указывает элемент данных.

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

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