companion object — mechanizm języka Kotlin, zastępujący słowo kluczowe static z Javy do deklarowania statycznych członków klasy. W Kotlin nie ma wbudowanego static — zamiast tego używa się deklaracji object z modyfikatorem companion wewnątrz klasy. Według JetBrains, 2025, companion object pozwala wywoływać metody i właściwości przez nazwę klasy, co czyni je pełnym odpowiednikiem statycznych członków w ekosystemie Javy.
Najważniejsze
companion object — to specjalna odmiana object declaration w języku Kotlin, która jest oznaczana słowem kluczowym companion. Pozwala ona deklarować członki należące do klasy, a nie do jej instancji — czyli statyczne członki w terminologii Javy. W przeciwieństwie do Javy, gdzie static jest modyfikatorem dla pojedynczych pól i metod, w Kotlin statyczne członki są grupowane wewnątrz jednego obiektu-towarzysza.
Każda klasa w Kotlin może zawierać dokładnie jeden companion object. Członki tego obiektu są dostępne przez nazwę klasy bez tworzenia instancji: MyClass.method(). Jednocześnie z perspektywy kodu bajtowego companion object jest kompilowany do oddzielnej wewnętrznej klasy, a jego metody stają się statycznymi metodami zewnętrznej klasy przy użyciu adnotacji @JvmStatic.
Według Google I/O 2024, companion object stał się głównym wzorcem dla metod fabrycznych, stałych i funkcji pomocniczych we współczesnych aplikacjach Android w Kotlin. Deweloperzy wybierają go zamiast klas narzędziowych z funkcjami pakietowymi, ponieważ companion object zachowuje logiczne powiązanie z klasą-właścicielem.
Używaj companion object do grupowania statycznego kontekstu — stałych, fabryk, metod pomocniczych, które logicznie odnoszą się do klasy, ale nie wymagają instancji.
Podstawowa składnia companion object jest prosta: słowo kluczowe companion stawiamy przed deklaracją object wewnątrz klasy. Jeśli nazwa nie jest podana, obiekt otrzymuje nazwę Companion, do którego można odwoływać się jawnie lub niejawnie.
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 }
}
// Dostęp przez nazwę klasy
val table = User.TABLE_NAME
val user = User.create("Alice")
W powyższym przykładzie TABLE_NAME i create() są dostępne przez User.TABLE_NAME i User.create(). Zwróć uwagę na modyfikator const dla prymitywnych stałych — gwarantuje on wbudowanie wartości na etapie kompilacji, analogicznie do Java static final dla primitives i String.
Jeśli companion object nie ma nazwy, można odwołać się do niego przez automatyczną nazwę Companion: User.Companion.TABLE_NAME. W praktyce jest to rzadko potrzebne, ale przydatne przy wywołaniach z kodu Javy lub przy użyciu refleksji.
// Oba wywołania są równoważne
User.create("Bob")
User.Companion.create("Bob")
// Pobranie referencji do companion object
val companion: User.Companion = User
companion object może mieć nazwę, co poprawia czytelność kodu i pozwala odwoływać się do niego po zrozumiałej nazwie. Factory — to najczęstsza nazwa dla companion object używanego jako fabryka.
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")
}
}
// Dostęp przez nazwę Factory
val response = HttpResponse.Factory.ok("data")
Nazwany companion object pomaga dokumentować przeznaczenie statycznych członków. Podczas refaktoryzacji nazwa czyni kod samodokumentującym się — deweloper od razu widzi, że Factory tworzy instancje HttpResponse.
Klasa może zawierać tylko jeden companion object. To zasadnicza różnica w stosunku do Javy, gdzie można zadeklarować dowolną liczbę statycznych pól i metod bez grupowania. Jeśli potrzebujesz kilku logicznych grup statycznych członków, używaj zagnieżdżonych object declarations bez companion.
Według danych oficjalnej dokumentacji Kotlin (Kotlin Docs, 2025), ograniczenie do jednego companion object wynika z projektu języka: statyczne członki powinny być ściśle powiązane z klasą, a jeden obiekt-towarzysz zapewnia wyraźną granicę tego powiązania. Jeśli statycznych członków jest zbyt wiele — to sygnał do refaktoryzacji klasy.
W Kotlin interfejsy również mogą zawierać companion object. Pozwala to definiować statyczne metody i stałe bezpośrednio wewnątrz interfejsu, co jest niemożliwe w Javie. Takie podejście jest często używane do deklarowania stałych związanych z interfejsem lub metod fabrycznych.
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)
}
}
W tym przykładzie BASE_URL, TIMEOUT_MS i create() należą do interfejsu ApiService, a nie do jego implementacji. Jest to wygodne: wszystkie stałe i logika fabryczna do tworzenia instancji serwisu API znajdują się w jednym miejscu — wewnątrz samego interfejsu.
Implementacje interfejsu nie dziedziczą członków companion object — są one wywoływane tylko przez nazwę interfejsu: ApiService.BASE_URL.
Przy wywoływaniu metod companion object z kodu Javy są one domyślnie dostępne jako metody zagnieżdżonej klasy Companion, a nie jako statyczne metody zewnętrznej klasy. Aby wyeksportować je jako prawdziwe static metody i pola dla Javy, użyj adnotacji @JvmStatic dla metod i @JvmField dla pól.
| Adnotacja | Zastosowanie | Wynik w Javie |
|---|---|---|
| @JvmStatic | Metody companion object | Statyczna metoda: ClassName.method() |
| @JvmField | Pola companion object | Statyczne pole: ClassName.field |
| const | Prymitywy i String | Inline-stała: kompilator podstawia wartość |
| bez adnotacji | Metody/pola domyślnie | 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"
}
}
// Wywołane w Javie jako MathUtils.square(5), MathUtils.TAG
Używanie @JvmStatic jest zalecane dla wszystkich publicznych metod companion object, które mają być dostępne z kodu Javy. const stosuje się tylko do typów prymitywnych i String — dla pozostałych typów używaj @JvmField.
W rzeczywistych projektach companion object jest używany do kilku standardowych scenariuszy. Metody fabryczne — to najczęstszy wzorzec: zamiast wielu konstruktorów z różnymi sygnaturami stosuje się nazwane metody fabryczne w 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 w NetworkResult dostarcza metodę fabryczną loading(), która tworzy instancję Loading z poprawnym typem generic. Bez tej metody trzeba by tworzyć instancję bezpośrednio przez Loading(), co ujawnia wewnętrzną implementację.
Często companion object jest używany do przechowywania stałych specyficznych dla klasy. Stałe zadeklarowane z const val są wbudowywane na etapie kompilacji, co daje zerowy narzut w czasie wykonania.
class UserRepository {
companion object {
private const val TAG = "UserRepository"
private const val CACHE_SIZE = 100
private const val DEFAULT_PAGE_SIZE = 20
}
}
Często zadawane pytania
ordinary object — to pełnoprawny singleton, istniejący niezależnie od klasy. companion object — to object wewnątrz klasy, którego członki są wywoływane przez nazwę zewnętrznej klasy. W kodzie bajtowym companion object staje się statyczną klasą zagnieżdżoną, a zwykły object — autonomiczną klasą-singletonem.
Nie, companion object nie jest dziedziczony przez podklasy. Jeśli klasa B dziedziczy po klasie A, to B.method() nie wywoła metody z companion object klasy A — należy odwoływać się przez A.method(). Odpowiada to zachowaniu statycznych metod w Javie.
Domyślnie — przez ClassName.Companion.method(). Aby wywoływać jako statyczną metodę, dodaj @JvmStatic do metody companion object. Dla pól używaj @JvmField lub zadeklaruj je z const val dla prymitywów.
Twórcy Kotlina postanowili zrezygnować z static na rzecz companion object dla jednolitej pracy z obiektami. static w Javie narusza zasady OOP, ponieważ statyczne metody nie są powiązane z instancją. companion object — to obiekt pierwszej klasy, który można przekazywać, rozszerzać funkcjami-rozszerzeniami i implementować interfejsy.
Przy użyciu const val dla prymitywów i String — zerowy narzut, wartość jest wbudowywana w kod bajtowy. Dla metod narzut nie występuje, jeśli metoda nie jest inline. W większości przypadków companion object nie tworzy mierzalnego wpływu na wydajność. Używaj @JvmStatic tylko dla publicznego API wywoływanego z Javy.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również