Зачем получать и устанавливать, если возвращаемое значение является изменчивым? - PullRequest
15 голосов
/ 12 сентября 2011

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

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

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

Ответы [ 5 ]

23 голосов
/ 12 сентября 2011

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


Образец 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 обычно полезен для построения неизменяемых объектов.

5 голосов
/ 12 сентября 2011

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

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

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

return new ArrayList<Foo>(foos);

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

return Collections.unmodifiableList(foos);

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

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

Поскольку:

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

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

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

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

0 голосов
/ 12 сентября 2011

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

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

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

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

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

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

0 голосов
/ 12 сентября 2011

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

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

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

...