Почему по умолчанию нельзя наследоваться от класса в Kotlin

Ответ

В Kotlin классы по умолчанию final (не наследуемые) для соблюдения принципа "закрытости/открытости" (Open/Closed Principle) из SOLID. Это предотвращает случайное наследование и модификацию поведения класса без явного разрешения. Чтобы разрешить наследование, нужно явно пометить класс как open:

open class Parent // Теперь можно наследоваться
class Child : Parent() // ОК

class NotOpenParent // По умолчанию final
// class ErrorChild : NotOpenParent() // Ошибка: Cannot inherit from final class

Преимущества:

  • Защита от неконтролируемого расширения
  • Упрощение рефакторинга (меньше зависимостей)
  • Явное проектирование иерархий через open

Ответ 18+ 🔞

Давай разберёмся, что за фигня тут происходит с этими классами в Kotlin. Представь, что ты пришёл в гости, а тебе говорят: "Вот этот стул — сиди, а вот этот шкаф — не трогай, он закрыт". Примерно так же Kotlin относится к классам по умолчанию.

Ёпта, они все final! То есть, запечатаны нахуй. Нельзя просто так взять и отнаследоваться, как в том же Java, где каждый класс — потенциальная проходная дворовая, готовая к расширению. А всё почему? А потому что какой-то умный дядька по имени Роберт Мартин (не путать с Мартином из "Назад в будущее") придумал принцип под названием "закрыто/открыто". Суть в том, что класс должен быть закрыт для модификаций, но открыт для расширений. А чтобы его расширить, нужно явно сказать: "Чувак, я тебе доверяю, делай со мной что хочешь". Иначе — никаких неожиданных наследников, которые могут всё сломать.

Вот смотри, как это выглядит в коде. Если ничего не указать — это стена:

class NotOpenParent // По умолчанию final, типа "не лезь, убьёт"
// class ErrorChild : NotOpenParent() // Ошибка! Нельзя наследоваться от final класса. Сам от такого охуеешь.

Компилятор тебе сразу: "Cannot inherit from final class NotOpenParent". Доверия ебать ноль, сразу видно.

А если ты действительно планировал большую и счастливую семью классов, то нужно крикнуть волшебное слово open:

open class Parent // Всё, я открыт! Наследуйтесь на здоровье, дети мои.
class Child : Parent() // И вот уже ребёнок есть. Всё легально, без подвохов.

В чём, блядь, профит от такой строгости?

  1. Защита от распиздяйства. Ты явно решаешь, какой класс можно расширять, а какой — нет. Никакой случайный коллега (или ты же через полгода) не нанаследует от твоего хитроумного HttpClient какую-нибудь MySuperHttpClientWithBlackjackAndHookers и не сломает всю логику. Подозрение ебать чувствую к таким неявным расширениям.

  2. Рефакторинг без сердечного приступа. Если класс final, ты знаешь, что его поведение не переопределено в десяти других местах проекта. Можешь спокойно менять его внутренности, не боясь, что где-то в глубине системы упадёт переопределённый метод doSomethingCritical(). Это терпения ноль ебать экономит.

  3. Явность — наше всё. Глядя на код, сразу видно, что open class — это точка расширения, проектировавшаяся для этого. А обычный class — это законченная, самодостаточная сущность. Архитектура становится читаемее. Не надо гадать, "какого хуя" этот класс вдруг унаследовали в другом модуле.

Короче, хуй с горы — это когда в Java всё открыто, и потом разбирайся, почему всё ломается. А в Kotlin подход "по умолчанию закрыто" — это как хороший замок на двери: не мешает жить, но от незваных гостей защищает. Сначала бесит, а потом понимаешь — охуенно же. Просто надо привыкнуть явно говорить open, когда это нужно.