Выражения и объявления объектов
Иногда нам нужно создать объект, являющийся небольшой модификацией какого-либо класса, не объявляя явным образом новый подкласс для него. 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
}
Если нам нужен «просто объект», без нетривиальных супертипов, мы можем просто сказать:
fun foo() {
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
}
Обратите внимание, что анонимные объекты могут использоваться как типы только в локальных и частных объявлениях. Если вы используете анонимный объект в качестве возвращаемого типа публичной функции или типа публичного свойства, фактический тип этой функции или свойства будет объявленным супертипом анонимного объекта или Any , если вы не объявили никакого супертипа. Члены, добавленные в анонимный объект, недоступны.
class C {
// Private function, so the return type is the anonymous object type
private fun foo() = object {
val x: String = "x"
}
// Public function, so the return type is Any
fun publicFoo() = object {
val x: String = "x"
}
fun bar() {
val x1 = foo().x // Works
val x2 = publicFoo().x // ERROR: Unresolved reference 'x'
}
}
Код в выражениях объектов может обращаться к переменным из окружающего пространства имен.
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++
}
})
// ...
}
Объявления объектов
Singleton может быть полезен в нескольких случаях, и 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()
}
}
К членам объекта-компаньона можно обращаться, используя просто имя класса в качестве квалификатора:
val instance = MyClass.create()
Имя объекта-компаньона можно опустить, в этом случае будет использовано имя Companion:
class MyClass {
companion object { }
}
val x = MyClass.Companion
Само имя класса (не в качестве квалификатора к другому имени) служит ссылкой на объект-компаньон класса (именованный или нет):
class MyClass1 {
companion object Named { }
}
val x = MyClass1
class MyClass2 {
companion object { }
}
val y = MyClass2
Обратите внимание, что, хотя члены объектов-компаньонов выглядят как статические члены в других языках, во время выполнения они всё ещё являются членами реальных объектов и могут, например, реализовывать интерфейсы:
interface Factory<T> {
fun create(): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}
val f: Factory<MyClass> = MyClass
Однако в JVM члены объектов-компаньонов могут быть сгенерированы как реальные статические методы и поля, если вы используете аннотацию @JvmStatic. Более подробную информацию см. в разделе взаимодействие с Java.
Семантические различия между выражениями и объявлениями объектов
Существует важное семантическое различие между выражениями и объявлениями объектов:
- выражения объектов выполняются (и инициализируются) немедленно, там, где они используются;
- объявления объектов инициализируются лениво, при первом доступе;
- объект-компаньон инициализируется при загрузке соответствующего класса (разрешении), что соответствует семантике статического инициализатора в Java.
© 2010–2020 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/reference/object-declarations.html