type | layout | category | title | url |
---|---|---|---|---|
doc |
reference |
Syntax |
Объекты |
Иногда нам необходимо получить экземпляр некоторого класса с незначительной модификацией, желательно без написания нового подкласса. Java справляется с этим с помощью вложенных анонимных классов. Kotlin несколько улучшает данный подход.
Для того, чтобы создать объект анонимного класса, который наследуется от какого-то типа (типов), используется конструкция:
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ...
}
override fun mouseEntered(e: MouseEvent) {
// ...
}
})
Если у супертипа есть конструктор, то в него должны быть переданы соответсвующие параметры. Множество супертипов может быть указано после двоеточия в виде списка, заполненного через запятую:
open class A(x: Int) {
public open val y: Int = x
}
interface B {...}
val ab: A = object : A(1), B {
override val y = 15
}
Если всё-таки нам нужен просто объект без всяких там родительских классов, то можем указать:
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
Код внутри объявленного объекта может обращаться к переменным за скобками так же, как вложенные анонимные классы в Java
fun countClicks(window: JComponent) {
var clickCount = 0
var enterCount = 0
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
clickCount++
}
override fun mouseEntered(e: MouseEvent) {
enterCount++
}
})
// ...
}
Синглтон - очень полезный паттерн программирования, и Kotlin (переняв у Scala) позволяет объявлять его довольно простым способом :
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ...
}
val allDataProviders: Collection<DataProvider>
get() = // ...
}
Это называется объявлением объекта и всегда имеет приставку в виде ключевого слова object. Аналогично объявлению переменной, объявление объекта не является выражением и не может быть использовано в правой части оператора присваивания.
Для непосредственной ссылки на объект используется его имя:
DataProviderManager.registerDataProvider(...)
Подобные объекты могут иметь супертипы:
object DefaultListener : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ...
}
override fun mouseEntered(e: MouseEvent) {
// ...
}
}
ПРИМЕЧАНИЕ: объявление объекта не может иметь локальный характер (т.е. быть вложенным непосредственно в функцию), но может быть вложено в объявление другого объекта или какого-либо невложенного класса.
Объявление объекта внутри класса может быть отмечено ключевым словом companion:
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
Для вызова членов такого companion
объекта используется имя класса:
val instance = MyClass.create()
Не обязательно указывать имя вспомогательного объекта. В таком случае он будет назван Companion
:
class MyClass {
companion object {
}
}
val x = MyClass.Companion
Такие члены вспомогательных объектов выглядят, как статические члены в других языках программирования. На самом же деле, они являются членами реальных объектов и могут реализовывать, к примеру, интерфейсы:
interface Factory<T> {
fun create(): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}
Однако в JVM вы можете статически генерировать методы вспомогательных объектов и полей, используя аннотацию @JvmStatic@
.
См. Совместимость с Java.
Существует только одно смысловое различие между этими двумя понятиями:
- анонимный объект инициализируется сразу после того, как был использован
- декларированный объект инициализируется лениво, в момент первого к нему доступа
- вспомогательный объект инициализируется в момент, когда класс, к которому он относится, загружен и семантически совпадает со статическим инициализатором Java