Companion object in Kotlin: cos'è, sintassi e utilizzo

Autore: IT Sectr Pubblicato: 2026-06-21 Tempo di lettura: 9 min

companion object è un meccanismo del linguaggio Kotlin che sostituisce la parola chiave static di Java per dichiarare membri statici di una classe. Kotlin non ha static integrato — invece, si usa una dichiarazione object con il modificatore companion all'interno di una classe. Secondo JetBrains, 2025, companion object permette di chiamare metodi e proprietà attraverso il nome della classe, rendendoli il pieno equivalente dei membri statici nell'ecosistema Java.

Punti Chiave

  • companion object è una dichiarazione object all'interno di una classe con la parola chiave companion, che sostituisce Java static
  • Membri statici accessibili tramite il nome della classe senza creare un'istanza: ClassName.method()
  • Companion object nominato permette di dare un nome all'oggetto per un codice più leggibile
  • Interfacce in Kotlin possono anch'esse contenere companion object con implementazione di metodi
  • @JvmStatic — annotazione per esportare i metodi di companion object come veri metodi static in Java

Cos'è companion object in Kotlin?

companion object è un tipo speciale di dichiarazione object in Kotlin contrassegnato dalla parola chiave companion. Permette di dichiarare membri che appartengono alla classe piuttosto che alle sue istanze — cioè membri statici in termini Java. A differenza di Java, dove static è un modificatore per campi e metodi individuali, in Kotlin i membri statici sono raggruppati all'interno di un unico oggetto compagno.

Qualsiasi classe in Kotlin può contenere esattamente un companion object. I membri di questo oggetto sono accessibili tramite il nome della classe senza creare un'istanza: MyClass.method(). In termini di bytecode, un companion object viene compilato come una classe interna separata e i suoi metodi diventano metodi statici della classe esterna quando si utilizza l'annotazione @JvmStatic.

Secondo Google I/O 2024, companion object è diventato il modello principale per metodi factory, costanti e funzioni di utilità nelle moderne applicazioni Android scritte in Kotlin. Gli sviluppatori lo scelgono rispetto alle classi di utilità con funzioni a livello di pacchetto perché companion object mantiene una connessione logica con la classe proprietaria.

Utilizza companion object per raggruppare il contesto statico — costanti, factory, metodi di supporto che appartengono logicamente alla classe ma non richiedono un'istanza.

Sintassi di companion object: dichiarazione e accesso

La sintassi di base di companion object è semplice: la parola chiave companion viene posta prima della dichiarazione object all'interno della classe. Se non viene specificato un nome, l'oggetto riceve il nome Companion, a cui si può accedere esplicitamente o implicitamente.

kotlin
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")

Nell'esempio sopra, TABLE_NAME e create() sono accessibili tramite User.TABLE_NAME e User.create(). Nota il modificatore const per le costanti primitive — garantisce l'inserimento del valore in fase di compilazione, simile a Java static final per primitivi e String.

Accesso implicito ed esplicito tramite Companion

Se un companion object non ha nome, è possibile accedervi tramite il nome automatico Companion: User.Companion.TABLE_NAME. In pratica questo è raramente necessario, ma utile quando si chiama da codice Java o quando si utilizza la riflessione.

kotlin
// Both calls are equivalent
User.create("Bob")
User.Companion.create("Bob")

// Getting reference to companion object
val companion: User.Companion = User

Companion object nominato e caratteristiche

Un companion object può avere un nome, il che migliora la leggibilità del codice e permette di accedervi con un nome significativo. Factory è il nome più comune per un companion object usato come factory.

kotlin
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 nominato aiuta a documentare lo scopo dei membri statici. Nel refactoring, il nome rende il codice auto-documentante — lo sviluppatore vede immediatamente che Factory crea istanze di HttpResponse.

Limitazione: esattamente un companion object per classe

Una classe può contenere un solo companion object. Questa è una differenza fondamentale rispetto a Java, dove puoi dichiarare qualsiasi numero di campi e metodi statici senza raggruppamento. Se hai bisogno di più gruppi logici di membri statici, utilizza dichiarazioni object annidate senza companion.

Secondo la documentazione ufficiale di Kotlin (Kotlin Docs, 2025), la limitazione di un solo companion object è motivata dalla progettazione del linguaggio: i membri statici devono essere strettamente accoppiati con la classe e un oggetto compagno fornisce un confine chiaro per questa connessione. Se ci sono troppi membri statici, è un segnale per rifattorizzare la classe.

Companion object nelle interfacce

In Kotlin, anche le interfacce possono contenere un companion object. Ciò consente di definire metodi statici e costanti direttamente all'interno di un'interfaccia, cosa non possibile in Java. Questo approccio viene spesso utilizzato per dichiarare costanti relative all'interfaccia o metodi factory.

kotlin
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)
    }
}

In questo esempio, BASE_URL, TIMEOUT_MS e create() appartengono all'interfaccia ApiService, non alle sue implementazioni. Questo è comodo: tutte le costanti e la logica factory per creare un'istanza del servizio API si trovano in un unico posto — all'interno dell'interfaccia stessa.

Le implementazioni dell'interfaccia non ereditano i membri del companion object — vengono chiamati solo tramite il nome dell'interfaccia: ApiService.BASE_URL.

@JvmStatic e @JvmField: compatibilità Java

Quando si chiamano metodi di companion object da codice Java, per impostazione predefinita sono accessibili come metodi della classe interna Companion, non come metodi statici della classe esterna. Per esportarli come veri metodi e campi statici per Java, usa l'annotazione @JvmStatic per i metodi e @JvmField per i campi.

AnnotazioneApplicazioneRisultato in Java
@JvmStaticMetodi di companion objectMetodo statico: ClassName.method()
@JvmFieldCampi di companion objectCampo statico: ClassName.field
constPrimitivi e StringCostante inline: il compilatore sostituisce il valore
senza annotazioneMetodi/campi predefinitiCompanion.method() / Companion.field
kotlin
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

Si raccomanda di usare @JvmStatic per tutti i metodi pubblici di companion object che devono essere accessibili da codice Java. const si applica solo ai tipi primitivi e String — per altri tipi usa @JvmField.

Esempi pratici di utilizzo di companion object

Nei progetti reali, companion object viene utilizzato per diversi scenari standard. I metodi factory sono il modello più comune: invece di molteplici costruttori con firme diverse, si utilizzano metodi factory nominati nel companion object.

kotlin
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 in NetworkResult fornisce un metodo factory loading() che crea un'istanza Loading con il tipo generico corretto. Senza questo metodo, dovresti creare un'istanza direttamente tramite Loading(), che espone l'implementazione interna.

Costanti e tag di logging

companion object viene spesso utilizzato per memorizzare costanti specifiche della classe. Le costanti dichiarate con const val vengono inserite in fase di compilazione, offrendo un overhead zero in fase di esecuzione.

kotlin
class UserRepository {
    companion object {
        private const val TAG = "UserRepository"
        private const val CACHE_SIZE = 100
        private const val DEFAULT_PAGE_SIZE = 20
    }
}

Domande Frequenti

Qual è la differenza tra companion object e un object normale in Kotlin?

Un object normale è un singleton a tutti gli effetti che esiste indipendentemente dalla classe. companion object è un object all'interno di una classe i cui membri vengono chiamati tramite il nome della classe esterna. In bytecode, un companion object diventa una classe annidata statica, mentre un object normale diventa una classe singleton autonoma.

Si può ereditare companion object?

No, companion object non viene ereditato dalle sottoclassi. Se la classe B estende la classe A, allora B.method() non chiamerà il metodo dal companion object della classe A — devi chiamare A.method(). Questo corrisponde al comportamento dei metodi statici in Java.

Come chiamare companion object da codice Java?

Per impostazione predefinita — tramite ClassName.Companion.method(). Per chiamarlo come metodo statico, aggiungi @JvmStatic al metodo del companion object. Per i campi, usa @JvmField o dichiarali con const val per i primitivi.

Perché non esiste la parola chiave static in Kotlin?

Gli sviluppatori di Kotlin hanno deciso di abbandonare static in favore di companion object per un lavoro uniforme con gli oggetti. static in Java viola i principi OOP perché i metodi statici non sono legati a un'istanza. companion object è un oggetto di prima classe che può essere passato, esteso con funzioni di estensione e implementare interfacce.

Companion object influisce sulle prestazioni?

Utilizzando const val per primitivi e String — overhead zero, il valore viene inserito nel bytecode. Per i metodi, non c'è overhead a meno che il metodo non sia inline. Nella maggior parte dei casi, companion object non crea alcun impatto misurabile sulle prestazioni. Usa @JvmStatic solo per API pubbliche chiamate da Java.

Riepilogo

  • companion object è un meccanismo Kotlin per dichiarare membri statici di classe, sostituendo Java static
  • Un companion object per classe — raggruppa tutti gli elementi statici in un unico oggetto compagno
  • Accesso tramite il nome della classe: ClassName.method() o ClassName.Companion.method()
  • @JvmStatic e @JvmField esportano i membri come veri metodi e campi statici per Java
  • const val per primitivi e String — inserisce il valore in fase di compilazione con overhead zero
  • Interfacce supportano anch'esse companion object per costanti e metodi factory
  • Companion object nominato migliora la leggibilità del codice e auto-documenta lo scopo dei membri statici

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche