Rzutowanie i kontrola typów
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?