Какой принцип SOLID нарушается при использовании final-классов в Java?

Ответ

Класс, объявленный как final, напрямую нарушает принцип Open/Closed Principle (OCP) — "классы должны быть открыты для расширения, но закрыты для модификации".

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

Пример проблемы:

// Final-класс нельзя унаследовать
final class PaymentProcessor {
    public void process(double amount) {
        System.out.println("Processing payment: " + amount);
    }
}

// Попытка расширить функциональность через наследование НЕВОЗМОЖНА:
// class AdvancedPaymentProcessor extends PaymentProcessor { // Ошибка компиляции
//     public void processWithFee(double amount) { ... }
// }

Как следовать OCP с final-классами? Принцип OCP можно соблюсти, используя не наследование, а композицию и интерфейсы:

// 1. Выделяем интерфейс
interface Processor {
    void process(double amount);
}

// 2. Final-класс реализует интерфейс
final class BasicPaymentProcessor implements Processor {
    @Override
    public void process(double amount) { /* базовая логика */ }
}

// 3. Расширяем функциональность через композицию и новый класс
class AdvancedPaymentProcessor implements Processor {
    private final Processor delegate; // Композиция
    public AdvancedPaymentProcessor(Processor delegate) {
        this.delegate = delegate;
    }
    @Override
    public void process(double amount) {
        delegate.process(amount); // Делегирование
        addTransactionFee(amount); // Расширение
    }
    private void addTransactionFee(double amount) { /* ... */ }
}

Ответ 18+ 🔞

Ага, слушай, вот эта вся хуйня про final классы и OCP — это вообще отдельная песня, блядь. Как будто специально придумали, чтобы мозги выебать.

Смотри, в чём прикол. Объявляешь ты класс как final — и всё, пиздец, нахуй. Принцип "открыт для расширения, закрыт для модификации" летит в пизду самым натуральным образом. Потому что расширять-то него нельзя, ёпта! Наследование запрещено на корню. Это как дверь нахуй заварили и сказали: "Вот твой функционал, сиди тут и не рыпайся".

Вот смотри, как это выглядит на практике:

// Финализировали класс — и пошла пизда
final class PaymentProcessor {
    public void process(double amount) {
        System.out.println("Processing payment: " + amount);
    }
}

// Попробуй теперь от этого говна унаследоваться — получишь по ебалу от компилятора
// class AdvancedPaymentProcessor extends PaymentProcessor { // Ошибка компиляции, сука!
//     public void processWithFee(double amount) { ... }
// }

Вот и сиди теперь с этим PaymentProcessor, как дурак. Хотел добавить комиссию или логирование — нихуя, блядь. Класс закрыт, как шлюха в понедельник.

Но, блядь, выход есть, ёпта! OCP — он не про наследование конкретно, а про расширяемость в целом. Наследование — это просто один, самый тупой, способ. Умные дядьки придумали композицию и интерфейсы, чтобы не быть такими максималистами, как этот Герасим из "Муму".

Вот как надо делать по-человечески:

// 1. Первым делом — интерфейс, мать его. Это наше всё.
interface Processor {
    void process(double amount);
}

// 2. Наш упоротый final-класс пусть реализует этот интерфейс.
// Он хоть и final, но контракт соблюдает.
final class BasicPaymentProcessor implements Processor {
    @Override
    public void process(double amount) { /* тут базовая логика, простая как три копейки */ }
}

// 3. А теперь, внимание, магия! Расширяем функциональность не через наследование, а через КОМПОЗИЦИЮ.
// Берём один процессор, заворачиваем в другой — и вуаля, овердохуища возможностей.
class AdvancedPaymentProcessor implements Processor {
    private final Processor delegate; // Вот она, композиция, блядь! Держим ссылку на того, кому будем делегировать.
    public AdvancedPaymentProcessor(Processor delegate) {
        this.delegate = delegate;
    }
    @Override
    public void process(double amount) {
        delegate.process(amount); // Сначала делаем базовую хуйню
        addTransactionFee(amount); // А потом уже свою, накрученную
    }
    private void addTransactionFee(double amount) { /* ... вот тут уже твоя фантазия */ }
}

Видишь, в чём фишка? BasicPaymentProcessor — он намертво зафинализирован, его не сломаешь. Но мы его не наследуем, а используем. Как инструмент. Берём этот простой кирпич и встраиваем в свою хитровыебанную стену. Хочешь — оберни в логирование, хочешь — в кэширование, хочешь — в три слоя безопасности. OCP соблюдён: базовый класс не трогали, но систему расширили до пизды.

Вот так-то, блядь. Не надо ломать дверь, если можно в окно залезть. Главное — мозги включить, а не тупо extends тыкать.