Дорогие читатели,
Помогите сделать документацию лучше!

К сожалению текст все еще содержит некоторое количество ошибок, и мы просим вас найти и исправить их. Если бы каждый из посетителей исправил хотя бы одну ошибку, то мы смогли бы довести до совершенства весь материал всего за один день.


Анонимные объекты и объявление объектов

Иногда нам необходимо получить экземпляр некоторого класса с незначительной модификацией, желательно без написания нового подкласса. Java справляется с этим с помощью вложенных анонимных классов. Kotlin несколько улучшает данный подход.

Анонимные объекты (ориг.:Object expressions)

Для того, чтобы создать объект анонимного класса, который наследуется от какого-то типа (типов), используется конструкция:

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++
        }
    })
    // ...
}

Объявления объектов (ориг.:Object declarations)

Синглтон - очень полезный паттерн программирования, и 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