Menu

Rzutowanie typów w Javie: rozszerzanie, zawężanie i konwersje

Jak Java zamienia wartości między typami: automatyczne rozszerzanie, jawne rzutowanie zawężające, jakie dane tracisz przy zawężaniu i konwersja między liczbami a stringami.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

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.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ