ProgrammazioneSviluppatore Kotlin

Как реализовано ключевое слово 'inline' применительно к классам (value class/inline class) в Kotlin? Какие ограничения существуют, как работают такие классы на уровне байткода, когда и почему их стоит использовать? Приведите пример и разъясните типичные сложности.

Supera i colloqui con l'assistente IA Hintsage

Risposta

In Kotlin inline class (a partire da Kotlin 1.5 — termine "value class"), consentono di creare wrapper sui tipi con costi minimi. Durante la compilazione, tali classi vengono sostituite, sotto il cofano, dal loro valore interno (value) per evitare il sovraccarico della creazione di oggetti.

Limitazioni e caratteristiche:

  • Può avere solo una proprietà nel costruttore primario.
  • Non è consentito memorizzare riferimenti all'uguaglianza referenziale (=== non funziona come al solito).
  • La value class non può essere una classe ereditabile e non può avere stati oltre il suo valore.
  • Non tutti i tipi generici e le API di piattaforma possono lavorare con inline/value class senza boxing.
  • La value class non può avere un blocco init, campi oltre il valore e solo funzioni minime.

Esempio:

@JvmInline value class UserId(val value: String) fun getUser(id: UserId) { println("Loading user with id: ${id.value}") } val id = UserId("XYZ") getUser(id) // Sotto il cofano funziona semplicemente con String!

Quando utilizzare:

  • Garantire la sicurezza dei tipi per identificatori, valori speciali.
  • Migliorare le prestazioni quando si operano con milioni di tali wrapper (gli oggetti non vengono creati).

Domanda trabocchetto

È possibile ereditare value class o utilizzarla in gerarchia di interfacce/classi astratte?

Risposta: No, la value class non può ereditare altre classi (a parte le interfacce), non può essere aperta per eredità, non consente il blocco init e altri campi non statici. L'unica opzione disponibile è implementare interfacce.

Esempio:

interface Validatable { fun isValid(): Boolean } @JvmInline value class Email(val raw: String) : Validatable { override fun isValid() = raw.contains("@") }

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


Storia

Un'app Android ha improvvisamente aumentato il tempo di avvio dopo l'aggiunta di value class ai parametri Parcelable: si è scoperto che un @Parcelize errato con value class portava a boxing/unboxing in ogni fase della serializzazione, vanificando i vantaggi dell'inlining.


Storia

Un microservizio ha iniziato a utilizzare attivamente value class per UserId e ProductId per la sicurezza dei tipi, ma in molti casi le funzioni generiche richiedevano la riflessione, che non funzionava con "il wrapper". I test unitari hanno iniziato a fallire inaspettatamente, si sono verificati ClassCastException.


Storia

Il codice migrato da Java ha iniziato a sostituire le classi di dominio interne con value class per l'ottimizzazione, ma il loro utilizzo come campi nullable ha portato a unexpected null pointer exception, poiché la value class può essere null solo se il valore esterno è anch'esso null, rompendo così i vecchi invarianti.