Решая задачи, я столкнулся с неожиданным поведением геттеров. В здешних лекциях и даже в генерируемых IntelliJ IDEA геттерах используется простейшая схема:
public final class A {
private A field;
public A getField() {
return field;
}
public void setField(A field) {
this.field = field;
}
}
Если поле примитивного типа или String, которые передаются по значению, то всё в порядке, но если объект передаётся по ссылке, то получается, что через геттер можно получить полный доступ к полю, что сводит на нет установленный уровень доступа protected.
Если в сеттере стоит какое-либо условие валидности, то через такой геттер его можно обойти, что нарушает инкапсуляцию и правильно было бы создавать клон объекта, что бы избежать этой неприятности.
Понимаю, что не все объекты просто клонировать и если это отдаётся на ответственность программиста, то странно почему об этом нигде в лекциях не встречалось упоминаний.
UPD:
Геттер, как идея, конечно же, не нарушает инкапсуляцию, но написан он должен быть так, что бы не предать принципов ООП. Это значит, что если геттер выдаёт объект, то таким образом, что бы пользователь не смог его изменить никаким другим способом, который прописан в классе.
В классе Collections существует замечательный метод public static Collection unmodifiableCollection(Collection c)
который выдаёт read-only коллекцию.
Это накладывает ряд других ограничений, например, невозможность сортировки, но на этот случай в этом классе есть:
unmodifiableSortedSet
unmodifiableSortedMap
и ещё несколько, для большего удобства.
Вы получаете геттером ссылку на экземпляр класса А. Класс А тоже имеет внутреннюю структуру, разные модификаторы доступа… Скажем, доступа к приватным полям класса А вы не получаете через геттер. Все норм… Геттер просто возвращает ссылку на значение в поле А… Что не так? В чем противоречие?
Если бы всё было в А скрыто — то какой толк в такой ссылке? Как с ней работать?
Вот например такой код:
Вывод будет таким:
Original: [one]
Changed: [one, two]
Not changed(safe Getter): [one, two]
Если в классе есть какой-то список, который нельзя пользователю класса менять, но есть геттер, как на примере выше, то через полученную таким геттером ссылку можно изменить этот список.
ПС: Пример тестил?
Короче, не вижу проблемы…
Давайте только необходимый доступ…
Но при таком геттере вполне возможно:
ArrayListtest = detField();
test.add(0);
И в конце поста я предполагаю, что достаточно написать более безопасный геттер, но меня изумляет, что тут в примерах методично используют вот такие небезопасные геттеры и ни разу эта ситуация не комментировалась в лекциях
То что указано в примерах — это ж примеры, они для простоты, особенно на старте… Не будьте так уж строги
return new ArrayList(Field)
Тут очень хорошие примеры, странно что к такому важному моменту не уделено должного внимания.
Возможно что на практике всё не так как в учебке, но это ещё только предстоит узнать.
К размышлению подтолкнула одна задача, в которой надо было изменить поле через полученную ссылку геттером, что, как мне кажется, не совсем адекватно.
Такое впечатление, что это получается как побочный результат, побочный и вредный.
+1 короче
аргументированоразвернуто каментить?))Почему ты считаешь, что нет? С таким успехом можно оставить поле public и не создавать геттеры и сеттеры.
Ну если ты создал геттеры и сеттеры то ты даешь доступ сам к полям (косвенный), не напрямую. и в этих методах можешь сделать себе нужные проверки. Да и методы можешь сделать не только публичные.
Геттер, который передаёт объект по значению не представляет опасности для инкапсуляции, но геттер передающий объект по ссылке — это же нарушение её.
Нужен или нет, ты сам решаешь, точно также реализацию. Главное чтобы не было прямого доступа к полям. А что открывать через методы, решаешь уже ты.
Мне интересно что думают на этот счёт знатоки, учитывая что тут в задачах очень часто используются геттеры, которые выдают ссылки на приватные поля. У меня есть предположение, почему ИДЕА генерирует такие геттеры (в конце поста написал) но удивляет, что в здешних лекциях про это ни слова не было.
Не хотелось бы спорить по терминам.
«По ссылке» я подразумеваю передачу указателя на объект в хиппе:
В джаве:
MyClass myClass;
тоже самое что в СИ:
MyClass *myClass;
Ссылки в джаве в каком то частном случае являюся указателями (Как в С). Но в си указатели всегда занимают 4 байта, и по сути это всегда инт, и в своей середине имеет адрес памяти куда ссылается.
В джаве там совсем по другому.
В джаве при передаче обьекта в метод как параметр, создается новая ссылка что указывает на тот же обьект, таким образом происходит передача по значению обьектов. Ссылки разные, обьект один и тот же.
Вернёмся к сабжу:
В примере метод editField() добавляет в список field неверный элемент «0».