Spec-Zone.ru › Kotlin 1.4

Выражения и объявления объектов

Иногда нам нужно создать объект, являющийся небольшой модификацией какого-либо класса, не объявляя явным образом новый подкласс для него. 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API