В Kotlin каждый класс может иметь один primary constructor (основной) и несколько secondary constructors (вторичных). Делегирование конструкторов — механизм, при котором secondary constructor обязательно должен вызвать либо primary constructor напрямую, либо другой secondary constructor того же класса (и в итоге — primary). Если класс наследует другой, каждый secondary constructor дочернего класса обязан явно делегировать вызов конструктору суперкласса, если таковой необходим.
В Java конструкторы могут напрямую вызывать друг друга с помощью this() или super(), перегружая конструкторы в различных комбинациях. В Kotlin эта концепция формализована: у класса может быть только один primary constructor, вторичные конструкты могут использовать логику делегирования, которую нужно явно указывать.
Некорректная реализация делегирования может привести к ошибкам компиляции: нельзя не вызывать primary constructor, если он объявлен, или не вызвать super-конструктор в наследуемом классе, если базовый класс не располагает дефолтным конструктором. Важно понимать, в какой момент вызываются init-блоки, как передаются параметры, и как делегирование влияет на порядок инициализации.
Пример базового делегирования:
class Person(val name: String) { constructor(name: String, age: Int) : this(name) { println("Secondary constructor: $name, $age") } }
Если используется наследование:
open class Parent(val name: String) class Child : Parent { constructor(name: String) : super(name) { println("Child secondary: $name") } }
Может ли secondary constructor не делегировать ни к чем?
Нет, компилятор потребует явно вызвать this(...) или super(...), иначе будет ошибка.
В каком порядке выполняются инициализации и init-блоки, если используется secondary constructor?
Первым всегда вызывается primary constructor и init-блок(и), затем код secondary constructor инициализации.
class Demo(val value: String) { init { println("init block") } constructor(value: String, code: Int) : this(value) { println("secondary: $code") } } Demo("kotlin", 7) // вывод: // init block // secondary: 7
Может ли наследник вызвать только primary constructor родителя, если есть его secondary constructor?
Да, но только явно, через super(...), secondary не видны наследникам напрямую.
В проекте secondary constructor не вызывает primary, жизненно важная инициализация (например, установка обязательных полей) не происходит, это становится причиной багов и падений в рантайме.
Плюсы:
Минусы:
Все secondary constructors строго делегируют через this(...), нужная инициализация централизована в primary/init, структура прозрачна для сопровождения.
Плюсы:
Минусы: