Зачем нужен 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 и многим другим.