ProgrammazioneSviluppatore Backend

Descrivi le caratteristiche delle dichiarazioni di costanti e del lavoro con gli oggetti companion in Kotlin, inclusi vincoli e sfumature.

Supera i colloqui con l'assistente IA Hintsage

Risposta.

La dichiarazione delle costanti e l'uso degli oggetti companion sono concetti importanti di Kotlin, che hanno sostituito i classici membri statici di Java e le difficoltà associate all'intersezione tra le paradigmi della programmazione orientata agli oggetti e della programmazione funzionale.

Contesto: In Java, per le costanti si utilizzano solitamente campi statici finali, mentre i metodi statici sono utilizzati per funzioni di utilità o di fabbrica. In Kotlin, invece di statico, sono stati introdotti object e companion object, e per le costanti a tempo di compilazione — la parola chiave const.

Problema: È necessario dichiarare valori che non dipendono dall'istanza della classe e organizzare metodi di fabbrica e stati statici senza compromettere l'integrità dell'OOP.

Soluzione: Gli oggetti companion (companion object) vengono dichiarati all'interno di una classe e consentono di posizionare membri comuni a tutte le istanze:

Esempio di codice:

class MyClass { companion object { const val DEFAULT_LIMIT = 10 fun create(): MyClass = MyClass() } } val limit = MyClass.DEFAULT_LIMIT val instance = MyClass.create()

Caratteristiche principali:

  • Tutto ciò che si trova all'interno dell'oggetto companion si comporta come membri statici in Java, ma mantiene l'integrazione OOP e la possibilità di ereditarietà/interfacce
  • Per le costanti a tempo di compilazione all'interno dell'oggetto companion è obbligatoria l'etichetta const
  • L'oggetto companion stesso è accessibile come oggetto (il riferimento può essere salvato) e può implementare interfacce

Domande trabocchetto.

Può un oggetto companion avere più istanze nella classe?

No, in una classe può esserci solo un oggetto companion. Tentare di dichiararne uno secondo porterà a un errore di compilazione. Tuttavia, all'interno dell'oggetto companion è consentito avere qualsiasi numero di metodi/proprietà.

È possibile inizializzare variabili lateinit all'interno di un oggetto companion?

No, perché le proprietà con const o le variabili all'interno dell'oggetto companion devono essere inizializzate immediatamente o devono essere val/var con inizializzazione esplicita. La parola chiave lateinit non è consentita per le proprietà all'interno dell'oggetto companion.

L'oggetto companion può avere un proprio nome e quando è necessario?

Sì, il nome dell'oggetto companion è specificato esplicitamente, se è necessario riferirsi ad esso per nome o, ad esempio, per implementare interfacce. In altri casi, è facoltativo. Esempio:

class Foo { companion object Factory { fun create(): Foo = Foo() } } val instance = Foo.Factory.create()

Errori tipici e anti-pattern

  • Utilizzare variabili statiche mutabili nell'oggetto companion — può portare a condizioni di gara nel codice multithreading
  • Mischiare costanti a tempo di compilazione (const) e valori a tempo di esecuzione in un unico oggetto
  • Oggetti companion non necessari, quando sono sufficienti funzioni/proprietà a livello di file

Esempio dalla vita reale

Caso negativo

Tutta la funzionalità ausiliaria e le variabili globali del programma vengono collocate nell'oggetto companion, utilizzando var invece di val/const:

Vantaggi:

  • Tutte le funzioni e variabili "statiche" in un unico posto, facilmente reperibili.

Svantaggi:

  • Difficoltà nei test, lo stato è globale e può essere accidentalmente modificato in altre parti del codice.

Caso positivo

Vengono utilizzate solo costanti a tempo di compilazione (const val) e funzioni pure all'interno dell'oggetto companion, tutto ciò che è mutabile è localizzato o passato tramite DI:

Vantaggi:

  • Codice prevedibile e sicuro, architettura chiara, aumenta il livello di incapsulamento.

Svantaggi:

  • A volte è necessario creare oggetti a livello di file per utilità globali, richiede un po' più di boilerplate.