Почему у Kotlin есть два типа конструкторов?
У Котлина есть два типа конструкторов, первичный и вторичный. Какова цель иметь два типа? На мой взгляд, это делает код более сложным и противоречивым. Если оба типа конструкторов создают объекты класса, они одинаково важны для класса.
Между тем, множественные инициализаторы также вносят путаницу и снижают читабельность.
3 ответа
Первичные конструкторы охватывают популярный случай использования, когда вам необходимо сохранить значения, переданные в качестве аргументов конструктора, в свойствах экземпляра.
По сути, первичный конструктор обеспечивает сокращение как для объявления свойства, так и для его инициализации из параметра конструктора.
Обратите внимание, что вы можете сделать то же самое без основных конструкторов:
class Foo {
val bar: Bar
constructor(barValue: Bar) {
bar = barValue
}
}
Но, поскольку это часто происходит в кодовых базах, первичные конструкторы Kotlin служат здесь для сокращения стандартного кода:
class Foo(val bar: Bar)
Вторичные конструкторы могут дополнять или заменять основной, чтобы поддерживать несколько процедур конструирования для одного класса.
Философия: основная цель - котлин это прагматичный язык. Основная идея этого: исключить наиболее частые шаблоны.
Многие классы, которые используются в C#/Java, имеют только один конструктор со следующей семантикой:
- Часть параметров сохраняется в полях (с одинаковыми именами)
- Часть параметров используется конструктором для создания другого поля (или дополнительной проверки)
Кроме того, много вторичных конструкторов используются для вызова основного конструктора со значениями по умолчанию (см. Этот вопрос для языка C#)
Следовательно: чтобы иметь упрощенный код (который отражает сущность), вы должны иметь возможность:
- Поддержка параметров конструктора, которые будут автоматически сохраняться в полях (без
this.myData = myData
) - Поддержка возможности создания поля из параметров конструктора
Оба вышеперечисленных элемента являются обязательными, поэтому все конструкторы имеют одинаковые входные значения (поскольку все поля должны быть инициализированы, однако они выводятся из тела конструктора). Поэтому у вас должен быть первичный конструктор, который будет выполнять инициализацию.
После применения этой логики мы получим главное правило: чтобы охватить наиболее частые сценарии инициализации класса, у вас должен быть первичный конструктор с возможностью определения значений параметров по умолчанию. Кроме того, вы должны иметь возможность создавать вторичные конструкторы для всех остальных сценариев.
Итак, я повторяю основную идею: первичный конструктор необходим для покрытия наиболее частых случаев (прагматическая цель)
Первичный конструктор может определить, какие параметры передаются в init
блоки. Например:
class Foo(a: Bar){
val b : Bar
init {
b = a // value of "a" is known from primary constructor
}
constructor(a: Boo) : this(a.toBar())
}
Без явного вызова первичного конструктора было бы невозможно определить, какое значение / тип a
следует использовать в init
,
Первичные блоки конструктора и инициализатора всегда выполняются перед вторичным блоком конструктора ( doc).