Omawiając dopasowanie wzorców w kotlinie użyliśmy konstrukcji when. W ramach niej pojawił się operator is, który jest operatorem sprawdzającym typ zmiennej i odpowiada, plus minus, instanceof z Javy. To czego nie mieliśmy okazji dotychczas sprawdzić to mechanizm śledzenia typu.

Śledzenie i automatyczne rzutowanie

Przeanalizujemy mały programik:

Listing 1. Przykład śledzenia typu

fun main(args: Array<String>) {

    val s:Any = "Hello"

    if(s is String)
        println(s.length)
    else
        println(s)
}

Dzięki użyciu is w wyrażeniu warunkowym kompilator może wywnioskować, że jeżeli wyrażenie jest prawdziwe, to zmienna jest danego typu i może ją bezpiecznie rzutować. Zatem w danym bloku kodu zmienna będzie miała inny typ. Mechanizm ten ma jednak pewne ograniczenia:

  • Lokalne niezmienne val – zawsze będą rzutowane,
  • właściwości niezmienne val – nie będą rzutowane jeżeli można rozszerzać, są oznaczone jako open, lub mają ręcznie napisane gettery,
  • lokalne zmienne var – jeżeli pomiędzy sprawdzeniem, a użyciem są efektywnie finalne,
  • właściwości zmienne var – nigdy, ponieważ mogą być zmienione w dowolnym momencie przez kod zewnętrzny.

Samodzielne rzutowanie

Trochę inaczej ma się sytuacja, gdy samodzielnie chcemy rzutować daną zmienną. W tym celu możemy użyć operatorów as oraz as?. Pierwszy z nich w przypadku niezgodności typów zwróci, klasycznie, CCE. Nie akceptuje on wartości null. Drugi z operatorów akceptuje wartość null, zachowana jest tu konwencja związana ze zmiennymi, które mogą być null. W przypadku przekazania takiej wartości zwróci null.

Zachowanie tych operatorów ilustruje poniższy program:

Listing 2. Użycie as i as?

fun main(args: Array<String>) {

    val s:Any = "Hello"

    println((s as String).length)
    println((null as? String))

}

Podsumowanie

Mechanizm śledzenia i automatycznego rzutowania jest, jak wiele elementów języka, nastawiony na redukcję ilości kodu, który musimy napisać. Nawet jego ograniczenia nie są jakoś specjalnie bolesne. Samodzielne rzutowanie… serio?