Зачем нужен companion object?

В Kotlin companion object используется для объявления статических членов класса, фабричных методов, констант, перехода между языками Kotlin и Java, а также для интеграции с определёнными библиотеками, которые требуют статического доступа к функциям или значениям. Поскольку в Kotlin нет static, companion object становится его идейным аналогом.

1. Что такое companion object

Это особый вид object, который объявляется внутри класса и имеет доступ к его приватным членам. Он создаёт один экземпляр (singleton), общий для всех объектов класса. К нему можно обращаться через имя класса.

Пример:

class User(val name: String) {
companion object {
const val MAX_NAME_LENGTH = 20
fun createGuest() = User("Guest")
}
}

Использование:

val guest = User.createGuest()
println(User.MAX_NAME_LENGTH)

2. Зачем нужен companion object

2.1. Статическая логика (как альтернатива static)

Kotlin сознательно отказался от ключевого слова static, предлагая вместо этого companion object. Это делает статическую логику более объектно-ориентированной: поведение оформляется как член специального объекта, а не как безликий статический метод.

2.2. Фабричные методы

Очень удобно инкапсулировать создание объектов через companion object:

class Rectangle(val width: Int, val height: Int) {
companion object {
fun fromSquare(side: Int): Rectangle {
return Rectangle(side, side)
}
}
}

Это часто используется как альтернатива конструкторам с разными параметрами.

2.3. Инкапсуляция приватного конструктора

class Secret private constructor(val value: String) {
companion object {
fun create(value: String): Secret {
return Secret(value)
}
}
}

Таким образом, объект нельзя создать напрямую — только через create.

2.4. Реализация интерфейса фабрики или сервиса

Companion object может реализовывать интерфейсы:

interface Factory<T> {
fun create(): T
}
class Foo {
companion object : Factory<Foo> {
override fun create(): Foo = Foo()
}
}

3. Доступ к private-членам внешнего класса

companion object может обращаться к приватным полям класса, в отличие от обычного object вне класса:

class Secret(val password: String) {
private fun hash() = password.reversed()
companion object {
fun hashPassword(secret: Secret): String {
return secret.hash() // доступ к private-функции
}
}
}

4. Интероперабельность с Java

Когда Kotlin-компилируемый класс должен использоваться в Java, companion object становится важным. Он компилируется как static-члены.

class MyUtil {
companion object {
@JvmStatic
fun greet() = println("Hello")
}
}

Из Java:

MyUtil.greet(); // работает благодаря @JvmStatic

Без @JvmStatic доступ будет через MyUtil.Companion.greet().

5. Именованные companion объекты

Можно давать companion object имя:

class Car {
companion object Factory {
fun create(): Car = Car()
}
}

В Kotlin можно обращаться как Car.create(), а в Java — как Car.Factory.create().

6. Импорт как синглтона

Можно импортировать companion object напрямую:

import Car.Companion.create
val car = create()

Это особенно полезно, когда нужно использовать методы без лишней квалификации.

7. Сравнение с обычным object

object (вне класса) companion object (внутри класса)
Глобальный синглтон Привязан к классу
--- ---
Нет доступа к приватным членам класса Имеет доступ
--- ---
Имя обязательно Имя опционально (по умолчанию Companion)
--- ---
Нельзя напрямую обращаться как ClassName.method() Можно так обращаться
--- ---

8. Пример использования companion object с константами

class Constants {
companion object {
const val BASE_URL = "https://api.example.com"
}
}

Константы можно использовать глобально:

println(Constants.BASE_URL)

9. Использование в DI, JSON, Room и других библиотеках

Некоторые библиотеки требуют статических методов:

  • Room (@TypeConverter)

  • Moshi (@FromJson/@ToJson)

  • Dagger/Hilt (@Provides)

  • Firebase (getInstance())

companion object с аннотациями типа @JvmStatic — способ соблюсти их требования.

10. Можно ли иметь несколько companion object?

Нет. В классе может быть только один companion object. Но он может содержать сколько угодно методов, значений, интерфейсов, классов и т.д.

companion object делает класс самодостаточным и позволяет в Kotlin элегантно выразить всё то, что в Java раньше приходилось выносить в отдельные утилитные классы или оформлять через static-члены. Он даёт возможность управления фабриками, приватными конструкторами, интерфейсами, интеграцией с Java и многим другим.