companion object est un mécanisme du langage Kotlin qui remplace le mot-clé static de Java pour déclarer des membres statiques d'une classe. Kotlin n'a pas de static intégré — à la place, on utilise une déclaration object avec le modificateur companion à l'intérieur d'une classe. Selon JetBrains, 2025, companion object permet d'appeler des méthodes et des propriétés via le nom de la classe, ce qui en fait l'équivalent complet des membres statiques dans l'écosystème Java.
Points Clés
companion object est un type spécial de déclaration object en Kotlin marqué par le mot-clé companion. Il permet de déclarer des membres qui appartiennent à la classe plutôt qu'à ses instances — c'est-à-dire des membres statiques en termes Java. Contrairement à Java, où static est un modificateur pour des champs et méthodes individuels, en Kotlin les membres statiques sont regroupés à l'intérieur d'un seul objet compagnon.
Toute classe en Kotlin peut contenir exactement un companion object. Les membres de cet objet sont accessibles via le nom de la classe sans créer d'instance : MyClass.method(). En termes de bytecode, un companion object est compilé en une classe interne séparée, et ses méthodes deviennent des méthodes statiques de la classe externe lors de l'utilisation de l'annotation @JvmStatic.
Selon Google I/O 2024, companion object est devenu le modèle principal pour les méthodes d'usine, les constantes et les fonctions utilitaires dans les applications Android modernes écrites en Kotlin. Les développeurs le choisissent plutôt que des classes utilitaires avec des fonctions au niveau du package car companion object maintient une connexion logique avec la classe propriétaire.
Utilisez companion object pour regrouper le contexte statique — constantes, usines, méthodes auxiliaires qui appartiennent logiquement à la classe mais ne nécessitent pas d'instance.
La syntaxe de base de companion object est simple : le mot-clé companion est placé avant la déclaration object à l'intérieur de la classe. Si aucun nom n'est spécifié, l'objet reçoit le nom Companion, qui peut être accédé explicitement ou implicitement.
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")
Dans l'exemple ci-dessus, TABLE_NAME et create() sont accessibles via User.TABLE_NAME et User.create(). Notez le modificateur const pour les constantes primitives — il garantit l'insertion de la valeur à la compilation, similaire à Java static final pour les primitifs et String.
Si un companion object n'a pas de nom, on peut y accéder via le nom automatique Companion : User.Companion.TABLE_NAME. En pratique, cela est rarement nécessaire, mais utile lors de l'appel depuis du code Java ou lors de l'utilisation de la réflexion.
// Both calls are equivalent
User.create("Bob")
User.Companion.create("Bob")
// Getting reference to companion object
val companion: User.Companion = User
Un companion object peut avoir un nom, ce qui améliore la lisibilité du code et permet d'y accéder par un nom significatif. Factory est le nom le plus courant pour un companion object utilisé comme usine.
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")
Un companion object nommé aide à documenter le but des membres statiques. En refactorisation, le nom rend le code auto-documenté — le développeur voit instantanément que Factory crée des instances de HttpResponse.
Une classe ne peut contenir qu'un seul companion object. C'est une différence fondamentale avec Java, où vous pouvez déclarer n'importe quel nombre de champs et méthodes statiques sans regroupement. Si vous avez besoin de plusieurs groupes logiques de membres statiques, utilisez des déclarations object imbriquées sans companion.
Selon la documentation officielle de Kotlin (Kotlin Docs, 2025), la limitation d'un seul companion object est motivée par la conception du langage : les membres statiques doivent être étroitement couplés avec la classe, et un objet compagnon fournit une limite claire pour cette connexion. S'il y a trop de membres statiques, c'est un signal pour refactoriser la classe.
En Kotlin, les interfaces peuvent également contenir un companion object. Cela permet de définir des méthodes statiques et des constantes directement à l'intérieur d'une interface, ce qui n'est pas possible en Java. Cette approche est souvent utilisée pour déclarer des constantes liées à l'interface ou des méthodes d'usine.
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)
}
}
Dans cet exemple, BASE_URL, TIMEOUT_MS et create() appartiennent à l'interface ApiService, pas à ses implémentations. C'est pratique : toutes les constantes et la logique d'usine pour créer une instance de service API se trouvent à un seul endroit — à l'intérieur de l'interface elle-même.
Les implémentations d'interface n'héritent pas des membres du companion object — ils sont appelés uniquement via le nom de l'interface : ApiService.BASE_URL.
Lors de l'appel de méthodes de companion object depuis du code Java, par défaut elles sont accessibles comme des méthodes de la classe interne Companion, pas comme des méthodes statiques de la classe externe. Pour les exporter comme de vraies méthodes et champs statiques pour Java, utilisez l'annotation @JvmStatic pour les méthodes et @JvmField pour les champs.
| Annotation | Application | Résultat en Java |
|---|---|---|
| @JvmStatic | Méthodes de companion object | Méthode statique : ClassName.method() |
| @JvmField | Champs de companion object | Champ statique : ClassName.field |
| const | Primitifs et String | Constante inline : le compilateur substitue la valeur |
| sans annotation | Méthodes/champs par défaut | 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
L'utilisation de @JvmStatic est recommandée pour toutes les méthodes publiques de companion object qui doivent être accessibles depuis du code Java. const s'applique uniquement aux types primitifs et String — pour les autres types, utilisez @JvmField.
Dans les projets réels, companion object est utilisé pour plusieurs scénarios standards. Les méthodes d'usine sont le modèle le plus courant : au lieu de plusieurs constructeurs avec des signatures différentes, on utilise des méthodes d'usine nommées dans 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 dans NetworkResult fournit une méthode d'usine loading() qui crée une instance Loading avec le bon type générique. Sans cette méthode, vous devriez créer une instance directement via Loading(), ce qui expose l'implémentation interne.
companion object est souvent utilisé pour stocker des constantes spécifiques à la classe. Les constantes déclarées avec const val sont insérées à la compilation, offrant une surcharge nulle à l'exécution.
class UserRepository {
companion object {
private const val TAG = "UserRepository"
private const val CACHE_SIZE = 100
private const val DEFAULT_PAGE_SIZE = 20
}
}
Questions Fréquentes
Un object normal est un singleton à part entière qui existe indépendamment de la classe. companion object est un object à l'intérieur d'une classe dont les membres sont appelés via le nom de la classe externe. En bytecode, un companion object devient une classe imbriquée statique, tandis qu'un object normal devient une classe singleton autonome.
Non, companion object n'est pas hérité par les sous-classes. Si la classe B étend la classe A, alors B.method() n'appellera pas la méthode du companion object de la classe A — vous devez appeler A.method(). Cela correspond au comportement des méthodes statiques en Java.
Par défaut — via ClassName.Companion.method(). Pour l'appeler comme méthode statique, ajoutez @JvmStatic à la méthode du companion object. Pour les champs, utilisez @JvmField ou déclarez-les avec const val pour les primitifs.
Les développeurs de Kotlin ont décidé d'abandonner static au profit de companion object pour un travail uniforme avec les objets. static en Java viole les principes POO car les méthodes statiques ne sont pas liées à une instance. companion object est un objet de première classe qui peut être passé, étendu avec des fonctions d'extension et implémenter des interfaces.
En utilisant const val pour les primitifs et String — surcharge nulle, la valeur est insérée dans le bytecode. Pour les méthodes, il n'y a pas de surcharge à moins que la méthode soit inline. Dans la plupart des cas, companion object ne crée aucun impact mesurable sur les performances. Utilisez @JvmStatic uniquement pour l'API publique appelée depuis Java.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi