companion object — механізм мови Kotlin, що замінює ключове слово static із Java для оголошення статичних членів класу. У Kotlin немає вбудованого static — натомість використовується оголошення object із модифікатором companion всередині класу. За даними JetBrains, 2025, companion object дозволяє викликати методи та властивості через ім'я класу, що робить їх повним аналогом статичних членів у Java-екосистемі.
Головне
companion object — це спеціальний різновид object declaration у мові Kotlin, який маркується ключовим словом companion. Він дозволяє оголосити члени, що належать класу, а не його екземплярам — тобто статичні члени в термінах Java. На відміну від Java, де static є модифікатором для окремих полів і методів, у Kotlin статичні члени групуються всередині одного об'єкта-компаньйона.
Будь-який клас у Kotlin може містити рівно один companion object. Члени цього об'єкта доступні через ім'я класу без створення екземпляра: MyClass.method(). З точки зору байт-коду companion object компілюється в окремий внутрішній клас, а його методи стають статичними методами зовнішнього класу при використанні анотації @JvmStatic.
За даними Google I/O 2024, companion object став основним патерном для фабричних методів, констант і службових функцій у сучасних Android-додатках на Kotlin. Розробники обирають його замість класів-утиліт із пакетними функціями, оскільки companion object зберігає логічний зв'язок із класом-власником.
Використовуйте companion object для групування статичного контексту — констант, фабрик, допоміжних методів, які логічно належать до класу, але не потребують екземпляра.
Базовий синтаксис companion object простий: ключове слово companion ставиться перед оголошенням object всередині класу. Якщо ім'я не вказано, об'єкт отримує ім'я Companion, до якого можна звертатися явно або неявно.
class User {
companion object {
const val TABLE_NAME = "users"
fun create(name: String): User {
return User(name)
}
}
val name: String
constructor(name: String) { this.name = name }
}
// Access via class name
val table = User.TABLE_NAME
val user = User.create("Alice")
У наведеному прикладі TABLE_NAME та create() доступні через User.TABLE_NAME та User.create(). Зверніть увагу на модифікатор const для примітивних констант — він гарантує вбудовування значення на етапі компіляції, аналогічно Java static final для примітивів і String.
Якщо companion object не має імені, до нього можна звернутися через автоматичне ім'я Companion: User.Companion.TABLE_NAME. На практиці це потрібно рідко, але корисно при виклику з Java-коду або при використанні рефлексії.
// Both calls are equivalent
User.create("Bob")
User.Companion.create("Bob")
// Getting reference to companion object
val companion: User.Companion = User
companion object може мати ім'я, що покращує читабельність коду та дозволяє звертатися до нього за зрозумілим іменем. Factory — найпоширеніше ім'я для companion object, що використовується як фабрика.
class HttpResponse {
companion object Factory {
fun ok(body: String): HttpResponse = HttpResponse(200, body)
fun notFound(): HttpResponse = HttpResponse(404, "Not Found")
fun serverError(): HttpResponse = HttpResponse(500, "Internal Error")
}
}
// Access via Factory name
val response = HttpResponse.Factory.ok("data")
Іменований companion object допомагає документувати призначення статичних членів. У рефакторингу ім'я робить код самодокументованим — розробник одразу бачить, що Factory створює екземпляри HttpResponse.
Клас може містити лише один companion object. Це принципова відмінність від Java, де можна оголосити будь-яку кількість статичних полів і методів без групування. Якщо потрібно кілька логічних груп статичних членів, використовуйте вкладені object declarations без companion.
За даними офіційної документації Kotlin (Kotlin Docs, 2025), обмеження на один companion object мотивоване дизайном мови: статичні члени повинні бути тісно пов'язані з класом, і один об'єкт-компаньйон забезпечує чітку межу цього зв'язку. Якщо статичних членів занадто багато — це сигнал до рефакторингу класу.
У Kotlin інтерфейси також можуть містити companion object. Це дозволяє визначати статичні методи та константи безпосередньо всередині інтерфейсу, що неможливо в Java. Такий підхід часто використовується для оголошення констант, пов'язаних з інтерфейсом, або фабричних методів.
interface ApiService {
companion object {
const val BASE_URL = "https://api.example.com"
const val TIMEOUT_MS = 5000
fun create(client: OkHttpClient): ApiService =
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.build()
.create(ApiService::class.java)
}
}
У цьому прикладі BASE_URL, TIMEOUT_MS та create() належать інтерфейсу ApiService, а не його реалізаціям. Це зручно: всі константи та фабрична логіка для створення екземпляра API-сервісу знаходяться в одному місці — всередині самого інтерфейсу.
Реалізації інтерфейсу не успадковують члени companion object — вони викликаються тільки через ім'я інтерфейсу: ApiService.BASE_URL.
При виклику методів companion object із Java-коду вони за замовчуванням доступні як методи вкладеного класу Companion, а не як статичні методи зовнішнього класу. Щоб експортувати їх як справжні static методи та поля для Java, використовуйте анотацію @JvmStatic для методів і @JvmField для полів.
| Анотація | Застосування | Результат у Java |
|---|---|---|
| @JvmStatic | Методи companion object | Статичний метод: ClassName.method() |
| @JvmField | Поля companion object | Статичне поле: ClassName.field |
| const | Примітиви та String | Inline-константа: компілятор підставляє значення |
| без анотації | Методи/поля за замовчуванням | Companion.method() / Companion.field |
class MathUtils {
companion object {
const val PI = 3.14159
@JvmStatic
fun square(x: Int): Int = x * x
@JvmField
val TAG: String = "MathUtils"
}
}
// Called in Java as MathUtils.square(5), MathUtils.TAG
Використання @JvmStatic рекомендується для всіх публічних методів companion object, які повинні бути доступні з Java-коду. const застосовується тільки до примітивних типів і String — для інших типів використовуйте @JvmField.
У реальних проектах companion object застосовується для кількох стандартних сценаріїв. Фабричні методи — найпоширеніший патерн: замість безлічі конструкторів із різними сигнатурами використовують іменовані фабричні методи в companion object.
sealed class NetworkResult<out T> {
data class Success<out T>(val data: T) : NetworkResult<T>()
data class Error(val message: String) : NetworkResult<Nothing>()
companion object {
fun loading<T>(): NetworkResult<T> =
Loading()
}
}
private class Loading<T> : NetworkResult<T>()
companion object у NetworkResult надає фабричний метод loading(), який створює екземпляр Loading із правильним generic-типом. Без цього методу довелося б створювати екземпляр напряму через Loading(), що розкриває внутрішню реалізацію.
Часто companion object використовується для зберігання констант, специфічних для класу. Константи, оголошені з const val, вбудовуються на етапі компіляції, що дає нульовий overhead у рантаймі.
class UserRepository {
companion object {
private const val TAG = "UserRepository"
private const val CACHE_SIZE = 100
private const val DEFAULT_PAGE_SIZE = 20
}
}
Часті запитання
Звичайний object — це повноцінний синглтон, що існує незалежно від класу. companion object — це object всередині класу, члени якого викликаються через ім'я зовнішнього класу. У байт-коді companion object стає статичним вкладеним класом, а звичайний object — автономним класом-синглтоном.
Ні, companion object не успадковується підкласами. Якщо клас B успадковує клас A, то B.method() не викличе метод із companion object класу A — потрібно звертатися через A.method(). Це відповідає поведінці статичних методів у Java.
За замовчуванням — через ClassName.Companion.method(). Щоб викликати як статичний метод, додайте @JvmStatic до методу companion object. Для полів використовуйте @JvmField або оголосіть їх із const val для примітивів.
Розробники Kotlin вирішили відмовитися від static на користь companion object для єдинообразної роботи з об'єктами. static у Java порушує принципи ООП, оскільки статичні методи не пов'язані з екземпляром. companion object — це об'єкт першого класу, який можна передавати, розширювати функціями-розширеннями та реалізовувати інтерфейси.
При використанні const val для примітивів і String — нульовий overhead, значення вбудовується в байт-код. Для методів overhead відсутній, якщо метод не є inline. У більшості випадків companion object не створює вимірного впливу на продуктивність. Використовуйте @JvmStatic тільки для публічного API, що викликається з Java.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також