Jason. Cała informatyka w jednym miejscu!

W poprzednim artykule podyskutowaliśmy o występujących typach danych w języku Kotlin. Dzisiaj chcę przybliżyć Wam dokładniej metody przenoszenia z jednego na drugi, tylko tym razem posiedzimy przy tym znacznie dłużej. Konwersja typów danych w języku Kotlin to jedyna droga, aby móc transformować dane na inny rodzaj. Weźmiemy także pod lupę problem przy przypisywaniu wartości liczbowych wykraczających poza zakres oraz jakie konsekwencje może przynieść konwersja z większego typu liczbowego na mniejszy.

KONWERSJA TYPÓW DANYCH W JĘZYKU KOTLIN. CZTERY PRZYPADKI

Zaczniemy od typów całkowitoliczbowych.

Z CAŁKOWITOLICZBOWEJ O MNIEJSZYM ZAKRESIE NA CAŁKOWITOLICZBOWĄ O WIĘKSZYM ZAKRESIE

Innymi słowy, załóżmy że mamy zmienną typu "Short":

val s: Short = 662

i chcemy sobie zamienić na "Int":

val i: Int = s

To nie przejdzie! Kotlin stawia na bezpieczeństwo w kodzie, a kompilator egzekwuje swoje prawo na bieżąco. Możemy jednak szybko ten kod naprawić dodając odwołanie do metody:

val i: Int = s.toInt()

Sukces. Oto przykładowa konwersja typów danych w języku Kotlin. Wystarczyło dodać metodę "toInt", żeby teraz wszystko było "cacy". Język poza zapewnieniem maksimum bezpieczeństwa, cechuje się również zwięzłością, aby nasze intencje zostały odczytane przy użyciu najmniejszej liczby słów kluczowych, co zostało napisane jakiś czas temu.

W tym przypadku nie będzie żadnych komplikacji. Liczba 662 mieści się w typie "Short" więc w "Int" zmieści się tym bardziej. Spróbujmy teraz w drugą stronę, czyli sytuacja kiedy przenosimy zbyt dużą liczbę do mniejszego zakresu.

Z CAŁKOWITOLICZBOWEJ O WIĘKSZYM ZAKRESIE NA CAŁKOWITOLICZBOWĄ O MNIEJSZYM ZAKRESIE

Sytuacja odwrotna. Ta sama liczba:

val s: Short = 662

tylko punkt docelowy zmienia się na "Byte":

val b: Byte = s.toByte()

Jasne, że to przejdzie tylko mam dla Was pytanie. Czy liczba uchowa się w takiej samej postaci? Nie! Na wyjściu zastaniecie wartość -106. Dlaczego? Ma to związek z przekroczeniem zakresu liczb całkowitych.

Przypadek liczby ukazanej powyżej to kod uzupełnienia do dwóch. To jest metoda obliczania liczby w systemie dziesiętnym przy użyciu liczby binarnej, w której pierwszy bit od lewej strony jest tak zwanym "bitem znaku". Gdy jest on zerem, wtedy liczba jest dodatnia. Kiedy wartość jest równa jeden, to wówczas zmienia się znak na przeciwny i liczba staje się ujemną. Z tego samego powodu powstaje podwójnie większy zakres liczb dla typu "unsigned" (bez znaku) po wyłączeniu liczb ujemnych. "Byte" w Kotlinie może przechowywać wartości w przedziale <-128; 127>, zatem liczba 662 przekracza ten zakres kilkukrotnie! Mimo wszystko, konwersja typów danych w języku Kotlin przejdzie, bo dajecie do zrozumienia że "bierzecie na klatę" wszelkie konsekwencje jakie mogą wystąpić.

Wytłumaczę Wam na chłopski rozum co się tu dzieje.

WYJAŚNIENIE "DZIWNEJ LICZBY"

Rozbijając liczbę 662 na sumę składników których podstawą jest dwójka, uzyskujemy taki ciąg:

128 + 128 + 128 + 128 + 128 + 22

Zaczynamy od punktu startowego jakim jest wartość 0. Dodajemy do tej liczby "pierwsze" 128, czyli mamy -128, nie +128! Ponieważ górny zakres to 127, dochodzi do "zapętlenia". O tym, dlaczego tak się dzieje, zachęcam do przeczytania stosownego artykułu. Po dodaniu drugiego składnika jakim jest także 128, mamy ponownie "powrót" do punktu startowego, czyli do zera. Widzimy, że mamy kolejną parę liczb 128 więc znowu "wejdzie" na -128 i powróci do cyfry 0. Po dodaniu "ostatniej" stu dwudziestki ósemki, licznik przechodzi znów na -128. Jeśli teraz do niego dodamy 22, otrzymujemy wynik -106. Poniżej zostawiam obliczenia krok po kroku:

0 + 128 = 128 => (128 > 127) => -128
-128 + 128 = 0
0 + 128 = 128 => (128 > 127) => -128
-128 + 128 = 0
0 + 128 = 128 => (128 > 127) => -128
-128 + 22 = -106

Innym podejściem do sprawy jest potraktowanie tego jak wyrażenie modulo, czyli wyznaczenie reszty z dzielenia. Wiedząc, że zakres od -128 do +127 wynosi 256 (włącznie z zerem), dzielimy liczbę w systemie dziesiętnym (w naszym przypadku 662) przez 256 i patrzymy jaka wyjdzie reszta z dzielenia.

662 / 256 = 2 reszty 150

Reszta wynosi 150. Teraz sprawdzamy czy ta reszta jest większa od maksymalnego dodatniego zakresu jakim jest 127. Jest! W takim razie do tych 150 dodajemy -256, zatem:

-256 + 150 = -106

Mamy ten sam wynik. W przypadku, gdyby reszta nie była większa od 127, wtedy ona sama staje się wynikiem końcowym. Wiem, że ciężko można to zrozumieć przy pierwszym podejściu, natomiast jeśli nie pociąga Was takie podejście, spróbujcie sięgnąć do naukowego i pouczyć się kodu uzupełniania do dwóch zwanym także "kodem U2".

W każdym razie, cel dydaktyczny osiągnięty, gdyż zaprezentowałem Wam jak daleko idące konsekwencje może mieć nieuwaga przy "przerzucaniu się" z typu danych o większym zakresie na typ danych o mniejszym zakresie. Uważajcie gdzie i w którym kierunku jest podejmowana konwersja typów danych w języku Kotlin!

Z CAŁKOWITOLICZBOWEJ NA ZMIENNOPRZECINKOWĄ

Wariant numer trzy. Przypadek konwersji z liczby całkowitej na zmiennoprzecinkową. Tę samą liczbę:

val s: Short = 662

możemy chcieć sobie przerobić na przykład na "Double", liczbę zmiennoprzecinkową podwójnej precyzji. Jak się do tego zabierzemy?

val d: Double = s.toDouble()

Jaki efekt? Do zmiennej "d" zostanie przypisana ta sama liczba, tylko "z ogonkiem" w postaci cyfry zero po przecinku. Nic więcej.

ZE ZMIENNOPRZECINKOWEJ NA CAŁKOWITOLICZBOWĄ

Co w przypadku, gdy zrobimy od drugiej strony i będziemy chcieli hipotetycznie taką liczbę:

val f = 368.62f

zamienić sobie na liczbę całkowitą "Integer"? Nie ma sprawy…

val i: Int = f.toInt()

...jednak pożegnajcie się z częścią ułamkową! I tu znowu może być dla niektórych zaskoczenie! Ułamek nie jest zaokrąglany, tylko "ścinany" (ang. "truncation"). Dochodzi do "obcięcia części ułamkowej" w wyniku czego uchowa się tylko liczba 368.

Gdybyście jednak chcieli sobie zaokrąglić liczbę zgodnie z zasadą matematyki, to na "happy end" macie metodę "roundToInt", dzięki czemu:

val i: Int = f.roundToInt()

będzie przechowywać już 369, chyba że ułamek jest mniejszy od 0,5.

Konwersja prostych typów danych w języku Kotlin

Stosując konwersje z jednego typu do drugiego trzeba bardzo uważać na to, jakie konsekwencje może to przynieść. Należy się zawsze upewnić czy wartość otrzymana na wyjściu jest przez nas oczekiwaną.


Na tym sobie zakończymy niniejszy artykuł. Konwersja typów danych w języku Kotlin wymaga uważania na wyżej opisane przypadki, abyście nie byli niemile zaskoczeni poszukiwaniem błędów w kodzie jak już go "oprawicie" w poboczne funkcjonalności. Mam nadzieję, że przyjemnie się czytało i coś z tego wyniesiecie więcej, a nie tylko "surprise" na twarzy.

PODOBNE ARTYKUŁY