Почему он так редко используется в дикой природе?
Потому что это не должно быть необходимо. Он также не полностью закрывает реализацию, поэтому может дать ложное чувство безопасности.
В этом нет необходимости из-за принципа замены Лискова . У метода есть контракт и в правильно спроектированной диаграмме наследования этот контракт выполнен (в противном случае это ошибка). Пример:
interface Animal {
void bark();
}
abstract class AbstractAnimal implements Animal{
final void bark() {
playSound("whoof.wav"); // you were thinking about a dog, weren't you?
}
}
class Dog extends AbstractAnimal {
// ok
}
class Cat extends AbstractAnimal() {
// oops - no barking allowed!
}
Не позволяя подклассу делать правильные вещи (для него), вы можете ввести ошибку. Или же вам может потребоваться, чтобы другой разработчик поместил дерево наследования вашего Garble
интерфейса рядом с вашим, потому что ваш последний метод не позволяет ему делать то, что он должен делать.
Ложное чувство безопасности типично для нестатического финального метода. Статический метод не должен использовать состояние из экземпляра (он не может). Нестатический метод, вероятно, делает. Ваш последний (нестатический) метод, вероятно, тоже имеет значение, но он не владеет переменными экземпляра - они могут отличаться от ожидаемых. Таким образом, вы добавляете бремя для разработчика формы наследования класса AbstractGarble
- чтобы гарантировать, что поля экземпляра находятся в состоянии, ожидаемом вашей реализацией в любой момент времени. Не давая разработчику возможности подготовить состояние перед вызовом вашего метода, как в:
int zblah() {
prepareState();
return super.zblah();
}
По моему мнению, вам не следует закрывать реализацию таким образом, если у вас нет очень веских причин. Если вы задокументируете свой контракт на метод и предоставите тестовый набор, вы сможете доверять другим разработчикам. Используя тест Junit, они могут фактически проверить принцип подстановки Лискова .
В качестве примечания я иногда закрываю метод. Особенно, если он находится на граничной части каркаса. Мой метод ведет некоторую бухгалтерию, а затем продолжает абстрактный метод, который будет реализован кем-то другим:
final boolean login() {
bookkeeping();
return doLogin();
}
abstract boolean doLogin();
Таким образом, никто не забывает вести бухгалтерию, но может предоставить пользовательский логин. Нравится ли вам такая настройка, конечно, зависит от вас:)