Объявление констант и использование companion objects — важные концепции Kotlin, пришедшие на смену привычным static-членам из Java и связанных с ними трудностей при пересечении парадигм ООП и функционального программирования.
История вопроса: В Java для констант обычно используют static final поля, статические методы — для служебных или фабричных функций. В Kotlin вместо static ввели object и companion object, а для compile-time констант — ключевое слово const.
Проблема: Нужно объявлять значения, не зависящие от экземпляра класса, а также организовать фабричные методы и статическое состояние, не нарушая целостности ООП.
Решение: Компаньон-объекты (companion object) объявляются внутри класса и позволяют размещать члены, общие для всех экземпляров:
Пример кода:
class MyClass { companion object { const val DEFAULT_LIMIT = 10 fun create(): MyClass = MyClass() } } val limit = MyClass.DEFAULT_LIMIT val instance = MyClass.create()
Ключевые особенности:
Может ли companion object иметь несколько экземпляров в классе?
Нет, в одном классе может быть только один companion object. Попытка объявить второй приведет к ошибке компиляции. Но внутри companion object допускается любое число методов/свойств.
Можно ли инициализировать lateinit переменные внутри companion object?
Нет, потому что свойства с const или переменные внутри companion object обязаны быть инициализированы сразу либо быть val/var с явной инициализацией. lateinit не разрешён для свойств внутри companion object.
Можно ли companion object иметь собственное имя и когда это требуется?
Да, имя companion object задаётся явно, если нужно обращаться к нему по имени или, например, реализовывать интерфейсы. В остальных случаях оно опционально. Пример:
class Foo { companion object Factory { fun create(): Foo = Foo() } } val instance = Foo.Factory.create()
Весь вспомогательный функционал и глобальные переменные программы размещаются в companion object, используются var вместо val/const:
Плюсы:
Минусы:
Используются только compile-time константы (const val) и pure functions внутри companion object, всё изменяемое либо локализовано, либо передано через DI:
Плюсы:
Минусы: