Co oznacza rzutowanie typów
Java ma typowanie statyczne: każda wartość ma typ, a kompilator nie pozwoli po cichu mieszać niezgodnych typów. Rzutowanie typów to sposób na zamianę wartości z jednego typu na inny. Czasem Java robi to za ciebie, a czasem trzeba o to poprosić wprost.
Są dwa kierunki. Przejście z mniejszego typu do większego (int do double) jest bezpieczne i dzieje się automatycznie. Przejście z większego do mniejszego (double do int) może stracić dane, więc Java wymaga, żeby zapisać je jawnie za pomocą rzutowania.
Rozszerzanie: konwersja automatyczna
Konwersja rozszerzająca przenosi wartość do typu, w którym jest więcej miejsca. Nic nie może zginąć, więc Java robi to za ciebie bez rzutowania:
int 7 trafia do double i staje się 7.0. Kolejność rozszerzania dla liczb to byte -> short -> int -> long -> float -> double; przypisanie od lewej do prawej wzdłuż tego łańcucha nigdy nie wymaga rzutowania. Dlatego mieszanie typów w arytmetyce często "po prostu działa": Java najpierw rozszerza mniejszy operand.
Zawężanie: jawne rzutowanie
W drugą stronę, przy konwersji zawężającej, dane mogą przepaść, więc kompilator odmawia, dopóki nie wpiszesz typu docelowego w nawiasie przed wartością:
(int) pi to rzutowanie. Zwróć uwagę, co robi: obcina w stronę zera, po prostu odrzucając część dziesiętną. 3.9 staje się 3, a -3.9 staje się -3. Nie zaokrągla. Jeśli chcesz zaokrąglić, potrzebujesz innego narzędzia:
Zapominanie, że rzutowanie obcina, to jeden z najczęstszych błędów początkujących: jeśli twoja "zaokrąglona" liczba zawsze wychodzi o jeden za mała, zwykle właśnie to jest powodem.
Pułapka dzielenia całkowitego
Na tę pułapkę każdy prędzej czy później się nadziewa. Gdy oba operandy są typu int, operator / wykonuje dzielenie całkowite i wyrzuca resztę, i to zanim nastąpi jakiekolwiek przypisanie do double:
a / b to 7 / 2, obliczone w całości w arytmetyce int, co daje 3. Późniejsze rozszerzenie do double zamienia tylko 3 na 3.0: .5 już przepadło. Rzutowanie jednego operandu na double na początku ((double) a / b) przenosi całe wyrażenie w świat liczb zmiennoprzecinkowych i daje oczekiwane 3.5. Wystarczy rzutować jedną stronę, a Java rozszerzy drugą, żeby pasowała.
Gdy zawężanie powoduje przepełnienie
Rzutowanie nie sprawdza, czy wartość się mieści. Jeśli się nie mieści, bity zostają po prostu ucięte i dostajesz zaskakujący wynik, bez błędu i bez ostrzeżenia:
300 nie mieści się w byte, więc wyższe bity zostają odrzucone i dostajesz 44. To "cicha" utrata danych: program działa bez zarzutu i daje złą odpowiedź. Zawężaj tylko wtedy, gdy masz pewność, że wartość zmieści się w mniejszym typie.
Konwersja między liczbami a stringami
String to obiekt, a nie liczba, więc nie da się go zrzutować przez (int): to błąd kompilacji. Do konwersji służą osobne metody:
Z tekstu na liczbę przechodzisz przez Integer.parseInt / Double.parseDouble. Jeśli napis nie jest poprawną liczbą (np. "hello"), te metody rzucają NumberFormatException. Z liczby na tekst użyj String.valueOf(n) albo po prostu dołącz "", bo n + "" zamienia int w String. Uważaj na różnicę między "5" + 1 (daje napis "51") a 5 + 1 (daje 6): + oznacza łączenie napisów, gdy tylko w grę wchodzi String.
Rzutowanie obiektów
Rzutowanie dotyczy też referencji do obiektów, nie tylko typów prostych. Referencję możesz zrzutować na spokrewniony typ w łańcuchu dziedziczenia: rozszerzenie do typu nadrzędnego jest automatyczne, a zawężenie z powrotem wymaga jawnego rzutowania i jest bezpieczne tylko wtedy, gdy obiekt naprawdę jest tego typu:
Object o = "hello";
String s = (String) o; // OK: o naprawdę wskazuje na String
Integer bad = (Integer) o; // kompiluje się, ale w czasie działania rzuca ClassCastException
Drugie rzutowanie się kompiluje, ale w czasie działania kończy się ClassCastException, bo obiekt jest typu String, a nie Integer. Poznasz to dokładnie, gdy dojdziesz do dziedziczenia i polimorfizmu; na razie wystarczy wiedzieć, że składnia (Type) to ten sam pomysł zastosowany do obiektów.
Najczęściej zadawane pytania
Czym jest rzutowanie typów w Javie?
Rzutowanie typów to zamiana wartości z jednego typu danych na inny. Małe konwersje "rozszerzające" (np. z int na double) Java wykonuje automatycznie, ale konwersja "zawężająca", która może stracić dane (np. z double na int), wymaga jawnego rzutowania: int n = (int) 3.9;.
Jak zamienić double na int w Javie?
Wpisz typ docelowy w nawiasie przed wartością: int n = (int) 3.9;. To obcina w stronę zera (odrzuca część dziesiętną), więc dostajesz 3, a nie 4. Jeśli chcesz zaokrąglić, użyj Math.round(3.9), które zwraca 4.
Jak zamienić String na int w Javie?
String nie jest liczbą, więc nie zrzutujesz go przez (int). Użyj Integer.parseInt("42"), żeby dostać int, albo Double.parseDouble("3.14") dla double. Jeśli tekst nie jest poprawną liczbą, te metody rzucają NumberFormatException.