ProgrammazioneSviluppatore Kotlin/Java

Come è strutturata l'ereditarietà dei costruttori in Kotlin, come funziona la chiamata al costruttore super e quali insidie ci sono nella combinazione tra costruttori primari e secondari? Fornisci esempi per diversi casi, spiega la differenza con Java.

Supera i colloqui con l'assistente IA Hintsage

Risposta

In Kotlin, una classe può avere un costruttore primario e quanti più costruttori secondari possibile. La differenza rispetto a Java è che in Kotlin il costruttore primario è dichiarato nell'intestazione della classe e può contenere parametri e modificatori. I costruttori secondari devono sempre chiamare un altro costruttore secondario, il costruttore primario o il costruttore della superclasse (tramite la parola chiave super).

Caratteristiche chiave:

  • Se la superclasse ha un costruttore primario con parametri obbligatori, la classe derivata deve sempre chiamarlo esplicitamente attraverso : super(...).
  • Nel costruttore secondario, la classe base viene chiamata tramite super(...) o un altro costruttore secondario/primario.
  • Non è permesso alcun conflitto di inizializzazione: tutti i campi devono sempre essere correttamente inizializzati.

Esempio di utilizzo con ereditarietà:

open class A(val a: Int) { constructor(a: Int, str: String) : this(a) { println("Secondario in A: $str") } } class B : A { constructor(a: Int) : super(a) { println("Secondario in B") } constructor(a: Int, s: String) : super(a, s) { println("Secondario #2 in B") } }

In cosa differisce l'approccio da Java:

  • In Kotlin non è possibile NON chiamare il costruttore della superclasse.
  • Il costruttore primario inizializza sempre i parametri di base e può essere implicito.

Domanda trabocchetto

È possibile in Kotlin creare una classe derivata SENZA chiamare esplicitamente il costruttore della superclasse, se nella classe base è presente solo un certo costruttore?

Risposta: No, a differenza di Java, in Kotlin la chiamata al costruttore della superclasse è obbligatoria e deve essere specificata esplicitamente sia nell'intestazione della classe che dopo la parola chiave : super().

Esempio di errore:

open class Base(val x: Int) class Derived : Base // Errore di compilazione!

Esempi di errori reali a causa dell'ignoranza delle sottilità dell'argomento


Storia

In un progetto microservizi dopo la migrazione a Kotlin, si è dimenticato di specificare esplicitamente il costruttore genitore: il costruttore della superclasse con un parametro obbligatorio non veniva chiamato, il servizio non si compilava e è stato necessario rifattorizzare le firme.


Storia

Un progetto Android aveva una profonda gerarchia (Activity → BaseActivity → CustomActivity), l'aggiunta di un costruttore secondario perdeva parametri, di conseguenza veniva chiamato il costruttore di base sbagliato e alcuni campi rimanevano null — l'applicazione falliva a runtime con NPE.


Storia

Nel codice di libreria aperto, un costruttore secondario nella classe derivata ha accidentalmente evitato il costruttore primario, causando due rami diversi di inizializzazione: a volte il campo era inizializzato, altre volte no. L'errore è stato trovato solo dopo molto tempo attraverso complessi report di bug.