Сохранение класса значений не окончательным для возможного расширения в будущем

Я создаю очень простой класс под названием Catalog. Это будет неизменяемый класс с полем идентификатора и имени.

По привычке, так как я не собираюсь явно документировать эту штуку для расширяемости, я поставил модификатор final на класс. Однако мне интересно, что, поскольку это такой простой класс значений, не помешает ли отключить последний модификатор на случай, если кто-то в будущем решит, что может его использовать?


person zztonedefzz    schedule 15.09.2014    source источник
comment
Является ли этот класс частью общедоступного API и используется кодом, который вы не контролируете? Если да, то вам обязательно стоит подумать, имеет ли смысл расширять класс или нет. Если нет, то оставьте его окончательным и спросите себя, стоит ли делать его не окончательным, когда вы чувствуете необходимость расширить класс.   -  person JB Nizet    schedule 15.09.2014
comment
Взгляните на stackoverflow.com /вопросы/218744/. В верхнем ответе есть хорошее резюме для расширяемости.   -  person mkobit    schedule 15.09.2014
comment
Если известны причины, обсуждать это не нужно. Это может закончиться только позиционированием, основанным на мнении.   -  person Holger    schedule 15.09.2014


Ответы (3)


На мой взгляд, хорошей практикой является создание простых типов значений final. Если вы хотите гарантировать неизменность, вам фактически нужно сделать это. Это также (частично), почему String, Integer и т. д. все final.

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

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

Обновление. Почему для неизменности требуется, чтобы класс был final? Конечно, есть и другие способы гарантировать неизменность определенного атрибута объекта.

Рассмотрим, например, класс RGBColor с тремя атрибутами red, green и blue типа byte. Делаем все три final и устанавливаем их в конструкторе раз и навсегда. (Мы можем дополнительно сделать их private и добавить соответствующие методы получения, но это не важно для данного обсуждения.) Конечно, мы переопределяем метод equals, чтобы он возвращал true тогда и только тогда, когда сравниваемый объект является экземпляром RGBColor с тем же самым red, green и blue значения.

Это выглядит невинно, но что, если кто-то решит расширить наш класс до класса RGBAColor, добавив атрибут alpha? Естественно, расширитель захочет переопределить equals, чтобы также принять во внимание значение alpha. Предположим, что наш расширитель также не очень заботится о неизменяемости и, таким образом, делает alpha не-final и предоставляет для него сеттер.

Теперь, если нам дан объект типа RGBColor, мы не можем с уверенностью предположить, что, если он сравнивается с другим, он все еще будет делать это через минуту. Мы могли бы предотвратить эту (конкретную проблему), также объявив equals как final в нашем классе RGBColor. Но тогда мы могли бы с таким же успехом сделать весь класс final, потому что расширение типа значения без возможности расширения понятия равенства практически бесполезно. (Есть и другие проблемы с переопределением equals, такие как несимметричность. Обычно мне это не очень удобно.)

person 5gon12eder    schedule 15.09.2014
comment
Так что, хотя мои поля являются частными, подкласс может перезаписать эти значения? Я не понимаю, как нарушается неизменность фактических значений. Однако я вижу, что переопределение одного из методов суперкласса может вернуть значение, отличное от того, которое установлено в конструкторе, так что с точки зрения клиента класс является изменчивым. Можете ли вы уточнить, пожалуйста. - person zztonedefzz; 16.09.2014

Вы сказали, что «он будет неизменным», поэтому сделайте его окончательным, чтобы класс нельзя было переопределить.

person sol4me    schedule 15.09.2014

Да, было бы больно отказаться от последнего модификатора. Опуская его, люди, которые используют ваш класс, не могут быть уверены, что он неизменен, и, следовательно, они не могут воспользоваться преимуществами неизменяемых объектов, например безопасностью потоков.

person Dave C    schedule 15.09.2014