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 }
}
// Достъп чрез името на класа
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 код или при използване на рефлексия.
// И двете извиквания са еквивалентни
User.create("Bob")
User.Companion.create("Bob")
// Получаване на референция към 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")
}
}
// Достъп чрез името Factory
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"
}
}
// Извиква се в Java като 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, се вграждат на етап компилация, което дава нулева допълнителна тежест по време на изпълнение.
class UserRepository {
companion object {
private const val TAG = "UserRepository"
private const val CACHE_SIZE = 100
private const val DEFAULT_PAGE_SIZE = 20
}
}
Често задавани въпроси
ordinary 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 — нулева допълнителна тежест, стойността се вгражда в байт-кода. За методи няма допълнителна тежест, ако методът не е inline. В повечето случаи companion object няма измеримо влияние върху производителността. Използвайте @JvmStatic само за публичен API, който се извиква от Java.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също