is, !is și smart cast în Kotlin

is verifică tipul unei valori; după verificare, compilatorul o tratează automat ca acel tip (smart cast).

Sintaxă
if (x is String) {
    x.length          // x este deja String aici
}
if (x !is Int) return
when (x) {
    is Int -> ...
    is String -> ...
}

Ce face is, !is și smart cast

x is Tip întoarce true dacă valoarea x este de tipul Tip (sau de un subtip al lui). !is este negația. Se folosește cu valori de tip general (Any, o interfață, o clasă sealed) ca să afli ce sunt de fapt.

Smart cast: după ce ai verificat tipul cu is (sau ai verificat x != null), compilatorul tratează automat variabila ca având tipul verificat, fără conversie manuală. Funcționează în if, în ramurile lui when, după && și după un return timpuriu: if (x !is String) return, apoi x.length.

Smart cast merge doar când compilatorul e sigur că valoarea nu se poate schimba între verificare și folosire: variabile val locale, parametri, proprietăți val din același modul fără getter propriu. La o proprietate var, copiază întâi valoarea într-o val locală.

Ce returnează

Boolean.

Exemple

Exemplul 1: Tipuri diferite într-o listă

Lista conține valori de tip Any. După is String, se poate folosi direct .length; după is Int, înmulțirea.

fun descrie(x: Any): String = when (x) {
    is String -> "text cu ${x.length} caractere"
    is Int -> "număr întreg, dublul este ${x * 2}"
    is Double -> "număr real, rotunjit ${Math.round(x)}"
    is List<*> -> "listă cu ${x.size} elemente"
    else -> "alt tip: ${x::class.simpleName}"
}

fun main() {
    val valori: List<Any> = listOf("Iași", 21, 9.75, listOf(1, 2, 3), true)
    for (v in valori) {
        println(descrie(v))
    }
}

Rezultat

text cu 4 caractere
număr întreg, dublul este 42
număr real, rotunjit 10
listă cu 3 elemente
alt tip: Boolean

Exemplul 2: Smart cast după && și după return

În dreapta lui &&, valoare este deja String. După if (... !is ...) return, tipul rămâne cunoscut până la finalul funcției.

fun esteCodPostal(valoare: Any): Boolean =
    valoare is String && valoare.length == 6 && valoare.all { it.isDigit() }

fun aplicaReducere(pret: Any) {
    if (pret !is Double) {
        println("prețul trebuie să fie Double, am primit $pret")
        return
    }
    println("cu 10% reducere: ${pret * 0.9} lei")
}

fun main() {
    println(esteCodPostal("700259"))
    println(esteCodPostal(700259))
    aplicaReducere(50.0)
    aplicaReducere("50")
}

Rezultat

true
false
cu 10% reducere: 45.0 lei
prețul trebuie să fie Double, am primit 50

Exemplul 3: Smart cast pe null și proprietăți var

Verificarea != null face și ea smart cast. Proprietatea var se copiază într-o val locală, altfel compilatorul refuză smart cast-ul.

class Comanda(var observatii: String?)

fun main() {
    val c = Comanda("sună la interfon")
    val obs = c.observatii          // copie locală
    if (obs != null) {
        println("Observații (${obs.length} caractere): $obs")
    }

    val nume: String? = listOf("Ana", "Dan").firstOrNull { it.startsWith("D") }
    if (nume != null && nume.length > 2) {
        println("Primul nume cu D: ${nume.uppercase()}")
    }
}

Rezultat

Observații (16 caractere): sună la interfon
Primul nume cu D: DAN

Greșeli frecvente

  • Smart cast pe o proprietate var: eroarea Smart cast to 'String' is impossible, because 'observatii' is a mutable property that could be mutated concurrently. Copiază valoarea într-o val locală.
  • x is List<String>: tipul elementelor nu se păstrează la rulare (type erasure). Poți verifica doar x is List<*>.
  • Verificarea unui tip pe care compilatorul îl știe deja ("text" is String): dă avertismentul check for instance is always 'true'.

Comenzi înrudite

  • as și as? (conversie de tip): as tratează o valoare ca alt tip (aruncă excepție dacă nu se potrivește); as? întoarce null în loc de excepție.
  • when: Alege o ramură după valoarea unei expresii sau după prima condiție adevărată; înlocuiește switch din alte limbaje.
  • null și null safety (?. ?: !!): null înseamnă „nicio valoare”. Kotlin separă tipurile care acceptă null (String?) și oferă ?., ?: și !! pentru ele.
  • sealed class și sealed interface: sealed declară o ierarhie închisă: toate subtipurile sunt cunoscute la compilare, deci when le poate verifica pe toate fără else.