Ce face sealed class și sealed interface
O clasă sau interfață sealed (sigilată) poate fi moștenită doar în același pachet și același modul în care a fost declarată. Compilatorul cunoaște deci toate subtipurile. Când faci when pe o valoare sealed și tratezi fiecare subtip, nu ai nevoie de else; dacă mai târziu adaugi un subtip, compilatorul arată toate when-urile care trebuie completate.
Este modul obișnuit în Kotlin de a modela stări sau rezultate care pot avea forme diferite: un răspuns de la server (succes cu date, eroare cu mesaj, încărcare), o plată (card, transfer, numerar), o comandă dintr-un meniu. Spre deosebire de enum, fiecare subtip poate avea alte proprietăți și pot exista mai multe obiecte din același subtip.
Subtipurile sunt de obicei data class (cu date) sau data object (fără date). O clasă sealed este abstractă și are constructorii protected sau private.
Exemple
Exemplul 1: Rezultatul unei operații
when tratează toate cele trei subtipuri, deci este exhaustiv fără else. Smart cast-ul face accesibile date și mesaj.
sealed interface Raspuns
data class Succes(val date: List<String>) : Raspuns
data class Eroare(val cod: Int, val mesaj: String) : Raspuns
data object Incarcare : Raspuns
fun afiseaza(r: Raspuns) = when (r) {
is Succes -> "Am primit ${r.date.size} produse: ${r.date}"
is Eroare -> "Eroare ${r.cod}: ${r.mesaj}"
Incarcare -> "Se încarcă..."
}
fun main() {
val raspunsuri = listOf(Incarcare, Succes(listOf("pâine", "lapte")), Eroare(404, "Magazinul nu există"))
raspunsuri.forEach { println(afiseaza(it)) }
}
Rezultat
Se încarcă...
Am primit 2 produse: [pâine, lapte]
Eroare 404: Magazinul nu există
Exemplul 2: Clasă sealed cu proprietate comună
Toate plățile au suma (din clasa sealed), dar fiecare subtip are datele lui. comision calculează diferit pentru fiecare.
sealed class Plata(val suma: Double) {
class Card(suma: Double, val banca: String) : Plata(suma)
class Transfer(suma: Double, val iban: String) : Plata(suma)
class Numerar(suma: Double) : Plata(suma)
}
fun comision(p: Plata): Double = when (p) {
is Plata.Card -> if (p.banca == "BT") 0.0 else p.suma * 0.01
is Plata.Transfer -> 2.5
is Plata.Numerar -> 0.0
}
fun main() {
val plati = listOf(Plata.Card(300.0, "ING"), Plata.Card(300.0, "BT"), Plata.Transfer(1200.0, "RO49AAAA1B31007593840000"), Plata.Numerar(50.0))
for (p in plati) println("${p::class.simpleName} ${p.suma} lei -> comision ${comision(p)} lei")
}
Rezultat
Card 300.0 lei -> comision 3.0 lei
Card 300.0 lei -> comision 0.0 lei
Transfer 1200.0 lei -> comision 2.5 lei
Numerar 50.0 lei -> comision 0.0 lei
Exemplul 3: Stările unui semafor cu tranziții
Fiecare stare știe care este următoarea. Ierarhia închisă garantează că urmatoarea tratează toate cazurile.
sealed interface Semafor {
data object Rosu : Semafor
data object Galben : Semafor
data class Verde(val secunde: Int) : Semafor
}
fun urmatoarea(s: Semafor): Semafor = when (s) {
Semafor.Rosu -> Semafor.Verde(30)
is Semafor.Verde -> Semafor.Galben
Semafor.Galben -> Semafor.Rosu
}
fun main() {
var s: Semafor = Semafor.Rosu
repeat(5) {
println(s)
s = urmatoarea(s)
}
}
Rezultat
Rosu
Verde(secunde=30)
Galben
Rosu
Verde(secunde=30)
Greșeli frecvente
- Adăugarea unui
elseîntr-unwhenpe o clasă sealed: compilează, dar pierzi avertizarea când apare un subtip nou. Lasăwhen-ul fărăelse. - Încercarea de a moșteni o clasă sealed dintr-un alt modul sau pachet: nu este permis; toate subtipurile se declară lângă ea.
- Folosirea lui
sealedpentru valori simple fără date: unenum classeste mai potrivit.
Comenzi înrudite
when: Alege o ramură după valoarea unei expresii sau după prima condiție adevărată; înlocuiește switch din alte limbaje.is, !is și smart cast: is verifică tipul unei valori; după verificare, compilatorul o tratează automat ca acel tip (smart cast).enum class: enum class declară un tip cu un număr fix de valori numite (zilele săptămânii, stările unei comenzi).data class: data class este o clasă pentru date: primește automat toString, equals, hashCode, copy și componentN.object: object declară o clasă cu un singur obiect (singleton) sau creează pe loc un obiect anonim care implementează o interfață.