Обучение на примере
Котлин: Конструкторы
Как и многие другие разработчики, работающие с Android, я начал писать приложения на Java, а затем перешел на Kotlin, поскольку он стал более широко принят Google и сообществом разработчиков Android.
Одно из первых отличий, с которыми я столкнулся при переходе с Java на Kotlin, заключалось в том, как определяются конструкторы.
В Java у вас есть ноль или более явно определенных конструкторов. Если вы не определяете конструктор, то для вас создается конструктор по умолчанию без аргументов. Конструкторы определены в теле класса Java, и каждый определенный конструктор отличается от других перегрузкой своего списка параметров.
И наоборот, в Kotlin у вас есть так называемый первичный конструктор, который необязательно определяется в сигнатуре класса. В дополнение к первичному конструктору вы можете определить ноль или более вторичных конструкторов.
Ниже приведен пример простого первичного конструктора в Котлине:
class Dog constructor(val name: String) {}
Здесь следует отметить несколько моментов:
- Использование ключевого слова constructor и его отображение в сигнатуре класса до определения тела. Это ключевое слово не является обязательным в этом примере и требуется только при использовании аннотаций (например,
@Inject
) или модификаторов видимости (например, при создании закрытого конструктора) - Основной конструктор не может содержать никакого кода. Код инициализации, который обычно появляется в этом конструкторе, вместо этого должен находиться в одном или нескольких блоках инициализации (init).
При наличии этой информации приведенный выше пример можно переписать, чтобы опустить ключевое слово конструктор, и результат будет таким же:
class Dog (val name: String)
Примером первичного конструктора, в котором действительно потребуется ключевое слово constructor, может быть класс, экземпляр которого вы не хотите создавать извне кода класса. Например, возможно, вы хотите, чтобы все новые экземпляры вашего класса Dog создавались с помощью фабричного метода:
class Dog private constructor(val name: String) { companion object { fun newDog(name: String) = Dog(name) } }
Еще одно различие между тем, как работают конструкторы в Kotlin по сравнению с Java, заключается в том, как они вызываются, или, другими словами, как создаются новые экземпляры. В Kotlin нет нового ключевого слова. Вместо этого аналогично создаются новые экземпляры класса:
val dog01 = Dog(name = "Sparky")
В Kotlin, если вы явно не определяете первичный конструктор, он будет создан для вас. Например, вполне допустим следующий код:
class EmptyDog
Новые экземпляры EmptyDog можно создать, вызвав сгенерированный первичный конструктор без аргументов следующим образом:
val emptyDog = EmptyDog()
Если вы хотите, чтобы при создании экземпляра вашего класса выполнялся какой-либо тип кода инициализации, вы должны сделать это с одним или несколькими блоками инициализации:
class Dog constructor(val name: String) { init { println("Registering $name with the AKC") registerDogWithAKC() } private fun registerDogWithAKC() { // TODO: perform registration tasks } }
Несколько замечаний по блокам инициализации:
- Блоки инициализации связаны с основным конструктором
- Независимо от того, определяете ли вы основной конструктор явно или нет, каждый определенный блок инициализации будет запускаться при создании экземпляра вашего класса.
- Если определено более одного блока инициализации, они будут выполняться в том порядке, в котором они появляются в теле вашего класса.
- На поля, которые появляются в основном конструкторе, можно ссылаться из блоков инициализации (в приведенном выше примере вы можете видеть, что на поле name есть ссылка в блоке инициализации)
- Если вы определили какие-либо вторичные конструкторы, обратите внимание, что определенные блоки инициализации будут выполняться до выполнения тела любого вторичного конструктора.
Если говорить о вторичных конструкторах, то теперь о них и поговорим :)
Из-за отсутствия аргументов по умолчанию в Java вы часто будете видеть анти-шаблон, известный как Telescoping Constructor, где конструкторы перегружаются снова и снова, придавая ему вид телескопирования в редакторе или диаграмме классов. Ниже приведен краткий пример этого антипаттерна:
private String name; private String breed; private boolean registered; public Dog() { this("Scruffy"); } public Dog(String name) { this(name, "Terrier"); } public Dog(String name, String breed) { this(name, breed, false); } public Dog(String name, String breed, boolean registered) { this.name = name; this.breed = breed; this.registered = registered; }
Этого анти-шаблона можно избежать, используя шаблон Builder, но в Kotlin в этом по большей части нет необходимости, потому что с Kotlin вы можете объявить значения по умолчанию для параметров конструктора:
class Dog (val name: String, val breed: String = "Terrier", val registered: Boolean = false)
С учетом сказанного есть еще много случаев, когда вы можете обнаружить, что вам нужно определить второй (или вторичный) конструктор или конструкторы.
Например, представьте конструктор копирования, который копирует значения существующего экземпляра Dog в новый экземпляр Dog:
class Dog (val name: String) { constructor(dog: Dog) : this(dog.name) } val dog01 = Dog(name = "Sparky") val dog02 = Dog(dog01)
Несколько замечаний по второстепенным конструкторам:
- Они должны начинаться с ключевого слова конструктор.
- Они должны вызывать первичный конструктор прямо или косвенно через другой вторичный конструктор. Вызов основного конструктора выполняется с помощью ключевого слова this, как показано в примере выше.
- Блоки инициализатора всегда будут выполняться до тела любых вторичных конструкторов.
- Параметры, указанные во вторичных конструкторах, не становятся атрибутами или полями класса. Они не могут иметь префикса var или val. Другими словами, вам нужно будет присвоить значения, переданные в поля, или что-то сделать с ними в теле вторичного конструктора (ов).
Заключение
Если вы работаете с Java, вы можете обнаружить, что конструкторы в Kotlin поначалу могут показаться немного сложными. Надеюсь, эта статья поможет вам в этом процессе обучения. Как всегда, просмотрите официальную документацию Kotlin и практикуйтесь, практикуйтесь, практикуйтесь, если хотите узнать больше.
Удачного кодирования!
Томас Сандерленд