Джошуа Блох в своей книге «Эффективная Java» рассказывает об этом. Он говорит «документ на наследство или запретить его».
Дело в том, что класс является своего рода контрактом между автором и клиентом. Разрешение клиенту наследовать от базового класса делает этот контракт намного более строгим. Если вы собираетесь наследовать от него, вы, скорее всего, собираетесь переопределить некоторые методы, в противном случае вы можете заменить наследование композицией. Какие методы могут быть переопределены, и что вы должны сделать для их реализации - должны быть задокументированы, иначе ваш код может привести к непредсказуемым результатам. Насколько я помню, он показывает такой пример - вот класс коллекции с методами
public interface Collection<E> extends Iterable<E> {
...
boolean add(E e);
boolean addAll(Collection<? extends E> c);
...
}
Существует некоторая реализация, то есть ArrayList. Теперь вы хотите наследовать от него и переопределить некоторые методы, чтобы он выводил на консоль сообщение при добавлении элемента. Теперь вам нужно переопределить add и addAll или только add ? Это зависит от того, как addAll реализован - работает ли он напрямую с внутренним состоянием (как это делает ArrayList) или вызывает add (как это делает AbstractCollection). Или, может быть, есть addInternal , который вызывается как add , так и addAll . Таких вопросов не было, пока вы не решили унаследовать этот класс. Если вы просто используете его - это вас не беспокоит. Таким образом, автор класса должен задокументировать это, если он хочет, чтобы вы унаследовали его класс.
А что если он захочет изменить реализацию в будущем? Если его класс используется, а не наследуется, ничто не мешает ему изменить реализацию на более эффективную. Теперь, если вы унаследовали от этого класса, посмотрели на источник и обнаружили, что addAll вызывает add , вы переопределяете только add . Позже автор меняет реализацию так, чтобы addAll больше не вызывал add - ваша программа не работает, сообщение не печатается при вызове addAll . Или вы посмотрели на источник и обнаружили, что addAll не вызывает add, поэтому вы переопределяете add и addAll . Теперь автор меняет реализацию, поэтому addAll вызывает добавить - ваша программа снова прерывается, когда addAll вызывается, сообщение печатается дважды для каждого элемента.
Итак, если вы хотите, чтобы ваш класс был унаследован, вам нужно задокументировать, как это сделать. Если вы думаете, что в будущем вам может понадобиться что-то изменить, что может нарушить некоторые подклассы - вам нужно подумать, как этого избежать. Позволяя вашим клиентам наследовать от вашего класса, вы раскрываете гораздо больше внутренних деталей реализации, которые вы делаете, когда просто позволяете им использовать ваш класс - вы раскрываете внутренний рабочий процесс, который часто подвергается изменениям в будущих версиях.
Если вы раскрываете некоторые детали и клиенты полагаются на них - вы больше не можете их изменить. Если с вами все в порядке или вы задокументировали, что можно, а что нельзя переопределить - это нормально. Иногда ты просто не хочешь этого. Иногда вы просто хотите сказать - «просто используйте этот класс, никогда не наследуйте его, потому что я хочу свободно изменять внутренние детали реализации».
Поэтому в основном комментарий «Потому что класс не хочет иметь детей, и мы должны уважать его желания», является правильным.
Итак, кто-то хочет пометить класс как окончательный / запечатанный, когда он считает, что возможные изменения в деталях реализации более ценны, чем наследование. Существуют и другие способы достижения результатов, аналогичных наследованию.