Как в Java запретить переопределение метода в классе-наследнике?

«Как в Java запретить переопределение метода в классе-наследнике?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Для запрета переопределения метода используется модификатор final.

1. Запрет переопределения конкретного метода: Объявите метод в родительском классе как final. Это гарантирует, что его реализация останется неизменной во всей иерархии наследования.

class PaymentService {
    // Этот метод нельзя изменить в наследниках
    public final void validateTransaction(Transaction t) {
        // Критичная бизнес-логика проверки
        if (t.getAmount() <= 0) {
            throw new IllegalArgumentException("Amount must be positive");
        }
    }

    public void processTransaction(Transaction t) { // Можно переопределить
        validateTransaction(t); // Всегда вызовется эта реализация
        // ... дальнейшая обработка
    }
}

class CustomPaymentService extends PaymentService {
    // ОШИБКА КОМПИЛЯЦИИ: Cannot override the final method
    // @Override
    // public void validateTransaction(Transaction t) { ... }
}

2. Запрет наследования всего класса: Объявите весь класс как final. Это предотвращает создание любых подклассов.

final class StringUtils { // От этого класса нельзя наследоваться
    public static boolean isNullOrEmpty(String s) {
        return s == null || s.isEmpty();
    }
}
// ОШИБКА КОМПИЛЯЦИИ: Cannot inherit from final 'StringUtils'
// class ExtendedStringUtils extends StringUtils { ... }

Зачем это нужно? (Принципы проектирования):

  • Инвариантность: Гарантирует корректное поведение критически важных методов (например, equals, hashCode, шаблонные методы).
  • Безопасность: Предотвращает изменение логики, которая может нарушить безопасность или целостность данных.
  • Производительность: Компилятор/JVM могут применять оптимизации (например, inline-подстановку) для final-методов.
  • Дизайн: Класс final часто используется для создания value-объектов (как String или Integer), которые должны быть неизменяемыми и предсказуемыми.