Классы
Классы в Kotlin объявляются с помощью использования ключевого слова class .
class Person < /*. */ >
Объявление класса состоит из имени класса, заголовка (указания типов его параметров, основного конструктора и т.п) и тела класса, заключённого в фигурные скобки. И заголовок, и тело класса являются необязательными составляющими. Если у класса нет тела, фигурные скобки могут быть опущены.
class Empty
Конструкторы
Класс в Kotlin может иметь основной конструктор (primary constructor) и один или более дополнительных конструкторов (secondary constructors). Основной конструктор является частью заголовка класса, его объявление идёт сразу после имени класса (и необязательных параметров).
class Person constructor(firstName: String) < /*. */ >
Если у основного конструктора нет аннотаций и модификаторов видимости, ключевое слово constructor может быть опущено.
class Person(firstName: String) < /*. */ >
Основной конструктор не может содержать в себе исполняемого кода. Инициализирующий код может быть помещён в соответствующие блоки (initializers blocks), которые помечаются словом init .
При создании экземпляра класса блоки инициализации выполняются в том порядке, в котором они идут в теле класса, чередуясь с инициализацией свойств.
class InitOrderDemo(name: String) < val firstProperty = "Первое свойство: $name".also(::println) init < println("Первый блок инициализации: $") > val secondProperty = "Второе свойство: $".also(::println) init < println("Второй блок инициализации: $") > >
Обратите внимание, что параметры основного конструктора могут быть использованы в инициализирующем блоке. Они также могут быть использованы при инициализации свойств в теле класса.
class Customer(name: String)
Для объявления и инициализации свойств основного конструктора в Kotlin есть лаконичное синтаксическое решение:
class Person(val firstName: String, val lastName: String, var age: Int)
Такие объявления также могут включать в себя значения свойств класса по умолчанию.
class Person(val firstName: String, val lastName: String, var isEmployed: Boolean = true)
Вы можете использовать завершающую запятую при объявлении свойств класса.
class Person( val firstName: String, val lastName: String, var age: Int, // завершающая запятая ) < /*. */ >
Свойства, объявленные в основном конструкторе, могут быть изменяемые ( var ) и неизменяемые ( val ).
Если у конструктора есть аннотации или модификаторы видимости, ключевое слово constructor обязательно, и модификаторы используются перед ним.
class Customer public @Inject constructor(name: String) < /*. */ >
Для более подробной информации см. «Модификаторы доступа».
Дополнительные конструкторы
В классах также могут быть объявлены дополнительные конструкторы (secondary constructors), перед которыми используется ключевое слово constructor .
class Person(val pets: MutableList = mutableListOf()) class Pet < constructor(owner: Person) < owner.pets.add(this) // добавляет этого питомца в список домашних животных своего владельца >>
Если у класса есть основной конструктор, каждый дополнительный конструктор должен прямо или косвенно ссылаться (через другой(ие) конструктор(ы)) на основной. Осуществляется это при помощи ключевого слова this .
class Person(val name: String) < val children: MutableList= mutableListOf() constructor(name: String, parent: Person) : this(name) < parent.children.add(this) >>
Обратите внимание, что код в блоках инициализации фактически становится частью основного конструктора. Дополнительный конструктор ссылается на основной при помощи своего первого оператора, поэтому код во всех блоках инициализации, а также инициализация свойств выполняется перед выполнением кода в теле дополнительного конструктора.
Даже если у класса нет основного конструктора на него все равно происходит неявная ссылка и блоки инициализации выполняются также.
class Constructors < init < println("Блок инициализации") >constructor(i: Int) < println("Constructor $i") >>
Если в абстрактном классе не объявлено никаких конструкторов (основного или дополнительных), у этого класса автоматически сгенерируется пустой конструктор без параметров. Видимость этого конструктора будет public.
Если вы не желаете иметь класс с открытым public конструктором, вам необходимо объявить пустой конструктор с соответствующим модификатором видимости.
class DontCreateMe private constructor () < /*. */ >
On the JVM, if all of the primary constructor parameters have default values, the compiler will generate an additional parameterless constructor which will use the default values. This makes it easier to use Kotlin with libraries such as Jackson or JPA that create class instances through parameterless constructors. > > «`kotlin > class Customer(val customerName: String = «») > «` —>
В JVM компилятор генерирует дополнительный конструктор без параметров в случае, если все параметры основного конструктора имеют значения по умолчанию. Это делает использование таких библиотек, как Jackson и JPA, более простым с Kotlin, так как они используют пустые конструкторы при создании экземпляров классов.
class Customer(val customerName: String = "")
Создание экземпляров классов
Для создания экземпляра класса конструктор вызывается так, как если бы он был обычной функцией.
val invoice = Invoice() val customer = Customer("Joe Smith")
Kotlin does not have a `new` keyword. —>
В Kotlin нет ключевого слова new .
Создание экземпляров вложенных, внутренних и анонимных внутренних классов описано в разделе Вложенные классы.
Члены класса
Классы могут содержать в себе:
- Конструкторы и инициализирующие блоки
- Функции
- Свойства
- Вложенные классы
- Объявления объектов
Наследование
Классы могут быть производными друг от друга и формировать иерархии наследования. Узнайте больше о наследовании в Котлине.
Абстрактные классы
Класс может быть объявлен как abstract со всеми или некоторыми его членами. Абстрактный член не имеет реализации в своём классе. Обратите внимание, что нам не надо аннотировать абстрактный класс или функцию словом open — это и так подразумевается.
abstract class Polygon < abstract fun draw() >class Rectangle : Polygon() < override fun draw() < // рисование прямоугольника >>
Можно переопределить неабстрактный open член абстрактным.
open class Polygon < open fun draw() < // некоторый метод рисования полигонов по умолчанию >> abstract class WildShape : Polygon() < // Классы, которые наследуют WildShape, должны предоставлять свой собственный // метод рисования вместо использования по умолчанию для полигона abstract override fun draw() >
Вспомогательные объекты
Если вам нужно написать функцию, которая может быть использована без создания экземпляра класса, имеющую доступ к данным внутри этого класса (к примеру, фабричный метод), вы можете написать её как член объявления объекта внутри этого класса.
В частности, если вы объявляете вспомогательный объект в своём классе, у вас появляется возможность обращаться к членам класса, используя только название класса в качестве классификатора.
© 2015—2023 Open Source Community
Когда вызывается конструктор у объекта kotlin
Для создания объекта необходимо вызвать конструктор класса. По умолчанию компилятор создает конструктор, который не принимает параметров и который мы можем использовать. Но также мы можем определять свои собственные конструкторы. Для определения конструкторов применяется ключевое слово constructor .
Классы в Kotlin могут иметь один первичный конструктор (primary constructor) и один или несколько вторичных конструкторов (secondary constructor).
Первичный конструктор
Первичный конструктор является частью заголовка класса и определяется сразу после имени класса:
class Person constructor(_name: String)
Конструкторы, как и обычные функции, могут иметь параметры. Так, в данном случае конструктор имеет параметр _name , который представляет тип String. Через параметры конструктора мы можем передать извне данные и использовать их для инициализации объекта. При этом первичный конструктор в отличие от функций не определяет никаких действий, он только может принимать данные извне через параметры.
Если первичный конструктор не имеет никаких аннотаций или модификаторов доступа, как в данном случае, то ключевое слово constructor можно опустить:
class Person(_name: String)
Инициализатор
Что делать с полученными через конструктор данными? Мы их можем использовать для инициализации свойств класса. Для этого применяются блоки инициализаторов:
class Person(_name: String) < val name: String init< name = _name >>
В классе Person определено свойство name, которое хранит имя человека. Чтобы передать эту свойству значение параметра _name из первичного конструктора, применяется блок инициализатора. Блок инициализатора определяется после ключевого слова init .
Цель инициализатора состоит в инициализации объекта при его создании. Стоит отметить, что здесь свойству name не задается начальное значение, потому это свойство в любом случае будет инициализировано в блоке инициализатора, и при создании объекта оно в любом случае получит значение.
Теперь мы можем использовать первичный конструктор класса для создания объекта:
fun main() < val tom = Person("Tom") val bob = Person("Bob") val alice = Person("Alice") println(tom.name) // Tom println(bob.name) // Bob println(alice.name) // Alice >class Person(_name: String) < val name: String init< name = _name >>
Важно учитывать, что если мы определили первичный конструктор, то мы не можем использовать конструктор по умолчанию, который генерируется компилятором. Для создания объекта обязательно надо использовать первичный конструктор, если он определен в классе.
Стоит отметить, что в классе может быть определено одновременно несколько блоков инициализатора.
Также стоит отметить, что в данном случае в инициализаторе нет смысла, так как параметры первичного конструктора можно нарямую передавать свойствам:
class Person(_name: String)
Первичный конструктор и свойства
Первичный конструктор также может использоваться для определения свойств:
fun main() < val bob: Person = Person("Bob", 23) println("Name: $Age: $") > class Person(val name: String, var age: Int)
Свойства определяются как и параметры, при этом их определение начинается с ключевого слова val (если их не планируется изменять) и var (если свойства должны быть изменяемыми). И в этом случае нам уже необязательно явным образом определять эти свойства в теле класса, так как их уже определяет конструктор. И при вызове конструктора этим свойствам автоматически передаются значения: Person(«Bob», 23)
Вторичные конструкторы
Класс также может определять вторичные конструкторы. Они применяются в основном, чтобы определить дополнительные параметры, через которые можно передавать данные для инициализации объекта.
Вторичные конструкторы определяются в теле класса. Если для класса определен первичный конструктор, то вторичный конструктор должен вызывать первичный с помощью ключевого слова this :
class Person(_name: String) < val name: String = _name var age: Int = 0 constructor(_name: String, _age: Int) : this(_name)< age = _age >>
Здесь в классе Person определен первичный конструктор, который принимает значение для установки свойства name :
class Person(_name: String)
И также добавлен вторичный конструктор. Он принимает два параметра: _name и _age. С помощью ключевого слова this вызывается первичный конструктор, поэтому через этот вызов необходимо передать значения для параметров первичного конструктора. В частности, в первичный конструктор передается значение параметра _name. В самом вторичном конструкторе устанавливается значение свойства age.
constructor(_name: String, _age: Int) : this(_name)
Таким образом, при вызове вторичного конструктора вначале вызывается первичный конструктор, срабатывает блок инициализатора, который устанавливает свойство name. Затем выполняются собственно действия вторичного конструктора, который устанавливает свойство age.
Используем данную модификацию класса Person:
fun main() < val tom: Person = Person("Tom") val bob: Person = Person("Bob", 45) println("Name: $Age: $") println("Name: $ Age: $") > class Person(_name: String) < val name: String = _name var age: Int = 0 constructor(_name: String, _age: Int) : this(_name)< age = _age >>
В функции main создаются два объекта Person. Для создания объекта tom применяется первичный конструктор, который принимает один параметр. Для создания объекта bob применяется вторичный конструктор с двумя параметрами.
Консольный вывод программы:
Name: Tom Age: 0 Name: Bob Age: 45
При необходимости мы можем определять и больше вторичных конструкторов:
fun main() < val tom = Person("Tom") val bob = Person("Bob", 41) val sam = Person("Sam", 32, "JetBtains") println("Name: $Age: $ Company: $") println("Name: $ Age: $ Company: $") println("Name: $ Age: $ Company: $") > class Person(_name: String) < val name = _name var age: Int = 0 var company: String = "Undefined" constructor(_name: String, _age: Int) : this(_name)< age = _age >constructor(_name: String, _age: Int, _comp: String) : this(_name, _age) < company = _comp >>
Здесь в класс Person добавлено новое свойство — company , которое описывает компании, в которой работает человек. И также добавлен еще один конструктор, который принимает три параметра:
constructor(_name: String, _age: Int, _comp: String) : this(_name, _age)
Чтобы не дублировать код установки свойств name и age, этот вторичный конструктор передает установку этих свойств другому вторичному конструктору, который принимает два параметра, через вызов this(_name, _age) . То есть данный вызов по сути будет вызывать первый вторичный конструктор с двумя параметрами.
Консольный вывод программы:
Name: Tom Age: 0 Company: Undefined Name: Bob Age: 41 Company: Undefined Name: Sam Age: 32 Company: JetBtains
Конструкторы класса в Kotlin
В примере прошлого урока при создании объекта мы ничего не передавали в класс для установки начальных значений свойств. Свойства инициировались в самом классе одинаково для всех объектов. После этого, чтобы поменять их значение у объекта, приходилось дополнительно обращаться к ним в функции main .
a.number = 10 a.step = 2
Куда удобнее, когда свойства сразу при создании объекта принимают необходимые значения. Для этого при вызове класса в скобках передаются значения, присваиваемые свойствам.
val a = NumInc(10, 2)
Однако создать объект таким образом, не изменив класс из предыдущего урока, не получится.
class NumInc { var number = 0 var step = 1 fun inc() { number += step } fun dec() { number -= step } }
В данном классе нет ничего, что говорило бы, что он принимает инициирующие значения для полей, и что их надо присваивать свойствам number и step .
Чтобы выражение NumInc(10, 2) работало, определить класс в Kotlin можно так:
class NumInc(n: Int, gap: Int) { var number = n var step = gap fun inc() { number += step } fun dec() { number -= step } }
Это похоже на то, как в заголовке функции описываются ее параметры. В теле класса их значения присваиваются переменным-свойствам. Однако классы – не функции, у них не может быть параметров. На самом деле это так называемый первичный конструктор, который есть в Kotlin, но не во многих других объектно-ориентированных языках. В Kotlin разработчики языка упростили синтаксис конструктора, вынеся его параметры в заголовок класса и назвав это первичным конструктором.
Вообще конструктор – это особый метод (функция-член) класса, который автоматически вызывается при создании объекта. Когда после имени класса мы ставим круглые скобки, именно они говорят компилятору, что мы хотим создать объект от данного класса. Если скобки не ставить, мы будем обращаться к самому классу как объекту (не создавая объекта-экземпляра), содержащему другие классы и объекты. Обращаться как к области видимости, подобно тому, как обращаемся к объектам, расположенным в другом файле пакета.
При обращении к классу круглые скобки после его имени – это вызов конструктора объекта, создаваемого от данного класса. Если в классе явно не определен ни один конструктор (ни первичный, ни вторичный), он ему будет добавлен автоматически – первичный и без параметров.
Объект можно создать только путем вызовом конструктора данного класса. Если конструктор – это по-сути функция, то как и любая другая функция, она-то и принимает аргументы, которые присваивает своим параметрам. Что дальше происходит с полученными значениями, внутреннее дело тела функции-конструктора. Обычно там эти значения присваиваются полям свойств создаваемого объекта.
Мы можем привести пример выше в более привычную для многих объектно-ориентированных языков форму:
class NumInc { var number: Int var step: Int constructor(num: Int, gap: Int) { number = num step = gap } fun inc() { number += step } fun dec() { number -= step } }
Здесь конструктор – это особая функция, которая определяется не через fun и имя функции, а через ключевое слово constructor . Функция constructor() будет неявно вызываться при создании объекта.
В Kotlin конструкторы, определенные в теле класса, называются вторичными. И если у класса нет первичного, то IntelliJ IDEA предложит преобразовать вторичный в первичный:
Согласившись, мы опять вернемся к предыдущей форме, когда параметры перечисляются в скобках после имени класса.
Обратите внимание, в примере со вторичным конструктором его можно конвертировать в первичный, если в заголовке определения класса после имени нет скобок. Это говорит о том, что у класса отсутствует первичный конструктор. Другими словами, если у класса всего один конструктор, почему бы не сделать его первичным.
Другое дело, если в заголовке после имени класса стоят круглые скобки, пусть даже пустые.
class NumInc() { .
Если при этом в классе есть метод constructor(), значит, что у класса два конструктора – первичный и вторичный. В этом случае вторичный нельзя конвертировать в первичный, иначе вы «убьете» уже имеющийся первичный. То, что первичный не имеет параметров, вопрос другой. Главное, что первичный конструктор определен.
У классов может быть несколько конструкторов. Это нужно для того, чтобы объекты одного и того же класса можно было создавать разными способами, то есть передавая разное количество начальных значений.
Допустим, в нашем примере мы хотим иметь возможность создавать объекты, как устанавливая значения свойств вручную, так и без этого, чтобы значения устанавливались автоматически. Например, должны работать оба варианта:
val a = NumInc(10, 2) val b = NumInc()
Подобное можно реализовать одним конструктором, установив значения по умолчанию.
class NumInc(num: Int = 0, gap: Int = 1) { var number: Int = num var step: Int = gap fun inc() { number += step } fun dec() { number -= step } }
Если параметрам функции присвоены значения, то в случае отсутствия аргументов при ее вызове будут использованы эти значения по-умолчанию.
Однако пойдем другим путем и определим два разных конструктора. Один с параметрами, другой без.
class NumInc() { var number: Int = 0 var step: Int = 1 constructor(num: Int, gap: Int): this() { number = num step = gap } fun inc() { number += step } fun dec() { number -= step } }
Первичный конструктор будет вызываться, когда объект создается без передачи значений свойств. Вторичный – когда значения передаются.
В заголовке вторичного конструктора ставится двоеточие, после которого пишется ключевое слово this , обозначающее текущий класс. Стоящие после this скобки говорят о вызове конструктора данного класса. В данном случае первичного, потому что в скобках ничего нет, а конструктор без параметров в нашем случае это первичный. То есть, если при создании объекта вызывается вторичный конструктор, ему должен предшествовать вызов первичного, если он имеется.
Выражение this() не всегда вызывает первичный конструктор. Может вызывать другой вторичный. Вызывается ли первичный или другой вторичный зависит от переданных аргументов. Однако если у класса есть первичный, вторичный всегда должен к нему обращаться напрямую или опосредовано через другой вторичный.
Откатим назад и вернемся к варианту с одним первичным конструктором.
class NumInc(n: Int, gap: Int) { var number = n var step = gap fun inc() { number += step } fun dec() { number -= step } }
Котлин позволяет определять свойства сразу в заголовке класса.
class NumInc(var number: Int, var step: Int) { fun inc() { number += step } fun dec() { number -= step } }
При создании объекта за сценой такого сокращенного синтаксиса происходит объявление свойств, вызов конструктора, и в нем присвоение переданных значений свойствам.
В заголовке мы заменили имена n и gap на number и step , но это не главное. Если бы раньше класс выглядел так
class NumInc(number: Int, step: Int) { var number = number var step = step . }
то number и step в заголовке были бы другими переменными, а не теми, что поля свойств. Просто одноименными. Это вполне рабочий вариант, однако чтобы различать что есть что, бывает лучше давать различные имена.
Главное, что изменилось, это появились модификаторы var (или val ). Они то и сообщают компилятору, что мы не просто определяем первичный конструктор, также одновременно объявляем свойства объекта (не обязательно все).
Рассмотрим вариант с первичным и двумя вторичными конструкторами:
fun main() { val a = NumInc(10, 2) val b = NumInc(5) val c = NumInc() a.inc() b.dec() c.inc() println("a: $") println("b: $") println("c: $") }
class NumInc(var number: Int, var step: Int) { constructor(num: Int) : this(num, 1) constructor() : this(0, 1) fun inc() { number += step } fun dec() { number -= step } }

Здесь в обоих вторичных конструкторах через this() мы вызываем первичный конструктор, так как только он принимает два аргумента. Однако с таким же успехом можно было из одного вторичного конструктора вызвать другой вторичный, который уже в свою очередь вызывает первичный.
class NumInc(var number: Int, var step: Int) { constructor(num: Int): this(num, 1) constructor(): this(0) .

Из вторичного конструктора, который без параметров, мы вызываем конструктор с одним параметром. А это другой вторичный, потому что первичный можно вызвать, только передав два аргумента.
Давайте снова вернемся к присвоению свойствам значений в теле класса:
class NumInc(num: Int, gap: Int) { var number = num var step = gap constructor(num: Int): this(num, 1) constructor(): this(0, 1) fun inc() { number += step } fun dec() { number -= step } }
Первичный конструктор заменим на еще один вторичный. Поскольку первичного нет, мы к нему не обращаемся через this() . Да и вторичные не делегируют друг к другу, каждый сам по себе.
class NumInc { var number: Int var step: Int constructor() { number = 0; step = 1} constructor(num: Int) { number = num; step = 0} constructor(num: Int, gap: Int) { number = num; step = gap} fun inc() { number += step } fun dec() { number -= step } }
(Примечание. Точка с запятой разделяют выражения, когда отсутствует перенос на новую строку.)
Такое определение класса в Kotlin схоже с «классическим», характерным для других языков, в которых нет такого понятия как первичный конструктор.
Объем кода можно уменьшить, сразу присвоив инициирующие значения свойствам:
class NumInc { var number = 0 var step = 1 constructor() constructor(num: Int) { number = num} constructor(num: Int, gap: Int) { number = num; step = gap} fun inc() { number += step } fun dec() { number -= step } }
Все эти примеры выражения одной и той же функциональности разными способами призваны показать, что написать класс можно по-разному. Много зависит от предпочтений программиста, удобства дальнейшего использования класса, будет ли он выступать родительским по отношению к другим.
При всем этом вариант с одним первичным конструктором, в котором полям задаются значения по умолчанию, в большинстве случаев является достаточным и предпочтительным:
class NumInc(var number: Int = 0, var step: Int = 1) { fun inc() { number += step } fun dec() { number -= step } }
Практическая работа:
Можно ли от класса, рассматриваемого в данном уроке, создать объект, передав в конструктор значение для свойства step , но не указав значение для number ?
X Скрыть Наверх
Введение в объектно-ориентированное программирование на Kotlin
Kotlin. Основной и вторичный конструкторы. Init блок.
Прежде чем разбираться в том, какие бывают конструкторы, нужно понять, что такое конструктор и в чём его предназначение. А для того, чтобы это понять нужно разобраться с другим определением, которое будет довольно часто встречаться и в документации Kotlin, и в различных статьях (в том числе моих). Речь идёт о свойствах.
Говоря простым языком, свойства — это те вещи, которые помогают идентифицировать класс. С их помощью класс описывается и ему задаются какие-либо параметры и значения.
Допустим, у нас есть класс Person . В качестве свойств у такого класса могут быть имя и возраст.
1 2 3 4 5
// класс Person со свойствами name и age class Person()
Свойств у класса может быть столько, сколько ему нужно. Но все они должны быть инициализированы при создании экземпляра этого класса. Поэтому для удобства был придуман конструктор — это специальный блок кода, который вызывается при создании экземпляра класса. Ему передаются необходимые значения, которые потом используются для инициализации свойств.
В Kotlin конструкторы бывают двух видов: основной и вторичный. У класса может и не быть конструктора, но Kotlin всё равно автоматически сгенерирует основной конструктор по умолчанию (без параметров).
Основной конструктор (primary constructor)
Также известен как первичный, главный, primary конструктор.
Объявляется он сразу после имени класса и состоит из ключевого слова constructor и круглых скобок.
1 2 3
class Person constructor(name: String, age: Int)
Если в классе нет иной логики, то фигурные скобки можно опустить.
class Person constructor(name: String, age: Int)
Можно обойтись и без ключевого слова constructor при условии, что нет аннотаций или модификаторов доступа.
class Person(name: String, age: Int)
Параметры, переданные в конструктор, можно использовать для инициализации свойств, объявленных в теле класса.
1 2 3 4
class Person(name: String, age: Int)
А можно упростить еще больше и из параметров конструктора сделать свойства класса. Для этого перед именем параметра нужно указать ключевое слова val (свойство только для чтения) или var (свойство для чтения и редактирования).
class Person(val name: String, var age: Int)
При этом любому из свойств можно присвоить значение по умолчанию. Тогда при создании экземпляра класса для этого свойства значение можно либо не указывать, либо указать, если оно отличается от стандартного.
1 2 3 4 5 6 7 8
class Person(val name: String, var age: Int = 30) . val adam = Person("Adam") val alice = Person("Alice", 25) println("$, $") // Adam, 30 println("$, $") // Alice, 25
Также из примера видно, что при создании нового экземпляра класса не нужно указывать слово new (как в Java). Достаточно вызвать его конструктор (даже если он пустой).
Если у всех параметров конструктора есть значение по умолчанию, то будет автоматически сгенерирован дополнительный конструктор без параметров, использующий значения по умолчанию. Это делается для того, чтобы упростить работу с библиотеками, которые для создания экземпляра класса используют конструктор без параметров.
У класса может быть суперкласс. Тогда его основной конструктор должен инициализировать свойства, унаследованные от суперкласса.
1 2 3 4 5 6 7 8
open class Base(p: Int) class Person(val name: String, var age: Int = 30, val p: Int) : Base(p) . val adam = Person("Adam", 30, 1000) println(adam.p) // 1000
Конструктор можно сделать приватным. Тогда никто и ничто не сможет создать экземпляр этого класса.
1 2 3 4
class Person private constructor(val name: String, var age: Int) . val adam = Person("Adam", 30) // вылетит ошибка
Вторичный конструктор (secondary constructor)
Также известен как вспомогательный, дополнительный, secondary конструктор.
Вторичный конструктор используется в том случае, когда необходимо определить альтернативный способ создания класса. В Kotlin это применяется редко, так как обычно основного конструктора бывает достаточно благодаря возможности добавлять значения по умолчанию и использовать именованные аргументы.
Объявляется вторичный конструктор внутри тела класса при помощи ключевого слова constructor .
1 2 3 4 5
class Person < constructor(id: Int) < . >>
При этом если у класса есть основной конструктор, то все вторичные конструкторы обязательно должны явно или косвенно его вызывать. Подразумевается, что либо вторичный конструктор сам вызывает основной конструктор, либо сначала вызывает другой вторичный конструктор, который в свою очередь обращается к основному конструктору. Обращение к основному конструктору осуществляется при помощи ключевого слова this .
1 2 3 4 5 6
class Person(val name: String, var age: Int) < constructor(name: String, age: Int, id: Int): this(name, age) < . >>
Если основного конструктора нет, то и обращаться к нему не надо.
Во вторичном конструкторе нельзя объявлять свойства класса. Все передаваемые ему параметры можно использовать либо для передачи основному конструктору, либо для инициализации свойств, объявленных в теле класса.
1 2 3 4 5 6 7
class Person(val name: String, var age: Int) < var id: Int = 0 constructor(name: String, age: Int, id: Int): this(name, age) < this.id= id >>
Также во вторичный конструктор можно добавить какую-либо логику.
1 2 3 4 5 6 7
class Person(val name: String, var age: Int) < var id: Int = 0 constructor(name: String, age: Int, id: Int): this(name, age) < if(id >0) this.id = id * 2 > >
Если у класса есть суперкласс, но нет основного конструктора, то каждый вторичный конструктор должен обращаться к конструктору суперкласса при помощи ключевого слова super .
1 2 3 4 5 6 7 8 9 10
open class Base(val p: Int) class Person : Base < constructor(name: String, age: Int, p: Int) : super(p) >. val adam = Person("Adam", 30, 1) println(adam.p) // 1
Блок инициализации (init блок)
Основной конструктор не может в себе содержать какую-либо логику по инициализации свойств (исполняемый код). Он предназначен исключительно для объявления свойств и присвоения им полученных значений. Поэтому вся логика может быть помещена в блок инициализации — блок кода, обязательно выполняемый при создании объекта независимо от того, с помощью какого конструктора этот объект создаётся. Помечается он словом init .
1 2 3 4 5 6 7 8 9 10 11 12 13
class Person(val name: String, var age: Int) < var id: Int = 0 // require выдает ошибку с указанным текстом, если условие в левой части false init < require(name.isNotBlank(), ) require(age > -1, ) > constructor(name: String, age: Int, id: Int): this(name, age) < if(id >0) this.id = id * 2 > >
По сути блок иницилизации — это способ настроить переменные или значения, а также проверить, что были переданы допустимые параметры.
Код в блоке инициализации выполняется сразу после создания экземпляра класса, т.е. сразу после вызова основного конструктора.
В классе может быть один или несколько блоков инициализации и выполняться они будут последовательно.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
class Person(val name: String, var age: Int) < // сначала вызывается основной конструктор и создаются свойства класса // далее вызывается первый блок инициализации init < . >// после первого вызывается второй блок инициализации init < . >// и т.д. >
Блок инициализации может быть добавлен, даже если у класса нет основного конструктора. В этом случае его код будет выпонен раньше кода вторичных конструкторов.
