artykuł logo
System dwójkowy
11 lutego 2016
dodatkowe zadania
Zestaw ćwiczeń dla początkujących programistów
13 marca 2016
Konwersja i rzutowanie

Cześć! W dzisiejszym artykule przeczytasz o konwersji i rzutowaniu w języku Java. Przeczytasz o konwersji obiektów oraz typów prostych. Dowiesz się czym jest czym jest konwersja bezstratna. Przeczytasz o konwersji typów do typu String. Poznasz mechanizm boxing’u oraz unboxing’u. Innymi słowy, dziś kolejna porcja informacji na temat języka Java 🙂

To jest jeden z artykułów w ramach darmowego kursu programowania w Javie. Proszę zapoznaj się z pozostałymi częściami, mogą one być pomocne w zrozumieniu materiału z tego artykułu.

Rzutowanie

Zaczniemy od rzutowania (ang. cast). Jak już wiesz kompilator Javy ma pewną wiedzę na temat tego jaki rodzaj obiektu kryje się pod daną referencją w trakcie kompilacji. Wie to z typu zmiennej do której przypisany jest dany obiekt. Jednak możliwa jest sytuacja kiedy pod referencją typu X przypisany jest obiekt typu Y jak w przykładzie poniżej:

Obie referencje są typu Object jednak druga z nich przechowuje zmienną typu String. Jest to w 100% poprawny kod. Jednak jeśli na zmiennej stringInstance chciałbyś wywołać metodę, którą implementuje klasa String a nie ma jej w klasie Object skończy się to błędem kompilacji:

Jak zatem wywołać taką metodę? Przecież jesteśmy pewni, że pod zmienną stringInstance kryje się obiekt typu String. Tutaj z pomocą przychodzi rzutowanie. Java pozwala rzutować typ A na typ B używając wyrażenia rzutowania, możesz je zobaczyć w przykładzie poniżej:

Oczywiście nie każde rzutowanie jest poprawne. W przykładzie poniżej możesz zobaczyć błędne rzutowanie z typu Object na typ String. Tego typu operacje kończą się wyjątkiem w trakcie wykonania programu:

Wyjątek mówi tyle, że obiektu typu java.lang.Object nie możemy rzutować do typu java.lang.String. Jakie rzutowanie jest w takim razie dozwolone? Możemy rzutować wyłącznie na typ, który znajduje się hierarchii dziedziczenia danego obiektu. Z tego właśnie powodu rzutowanie String na Object jest dopuszczalne ale odwrotna operacja kończy się błędem.

W jednym z kolejnych artykułów przeczytasz o typach generycznych, które pomagają rozwiązać część sytuacji, kiedy rzutowanie jest potrzebne. W codziennym programowaniu radziłbym unikać tego typu operacji. Z pewnością istnieje inny sposób napisania programu, który pozwoli na uniknięcie rzutowania.

Konwersja

Rzutowanie to specyficzny przypadek konwersji, jest to konwersja jawna, wymuszona przez programistę. W trakcie niektórych operacji może dochodzić do automatycznej konwersji, konwersji niejawnej. Konwersja niejawna może wystąpić np. podczas wywołania metod czy operacji arytmetycznych. Poniżej przykład konwersji automatycznej, która zachodzi w trakcie wywołania metody:

Dochodzi do konwersji ponieważ metoda methodLongArgument przyjmuje parametr typu long a jedno z wywołań przyjmuje zmienną typu int. Jest to tak zwana konwersja rozszerzająca. Może być wykonana niejawnie ponieważ podczas takiej konwersji nie zachodzi ryzyko utracenia informacji (o tym dalej). Kompilator robi to automatycznie za programistę. W przykładzie powyżej zmienna intVariable zostałą automatycznie rozszerzone do typu long. Nie utraciliśmy żadnych informacji ponieważ typ long zawsze może pomieścić liczby które przechowuje int. W przykładzie używam zmiennych statycznych MAX_VALUE, które są typu int lub long i trzymają największą liczbę możliwą do przechowywania przez dany typ.

Konwersja w odwrotną stronę wymaga już jawnego rzutowania. Taka konwersja może prowadzić do utraty informacji. Proszę spójrz na przykład:

Jak myślisz co zostanie wypisane na konsoli po uruchomieniu tego programu?

Dziwne prawda? 🙂 Środkowa linijka to nic innego jak właśnie „utrata informacji”, która może zajść w trakcie jawnej konwersji 1. Ostatnia linijka pokazuje, że nie każda konwersja z long do int prowadzi do utraty informacji.

Konwersja typów zmiennoprzecinkowych do całkowitoliczbowych

Innym przykładem konwersji w której dochodzi do utraty informacji jest konwersja z typów zmiennoprzecinkowych do typów całkowitoliczbowych:

W obu przypadkach tracimy informację o ułamku, zostaje wyłącznie część całkowitoliczbowa.

Automatyczna konwersja podczas przypisania

Podobnie rzecz się ma w przypadku przypisania wartości zmiennej, tutaj także dochodzi do automatycznej konwersji. Poniższy przykład pokazuje kilka możliwych przypadków:

Pierwsza linijka metody main to niejawna konwersja z typu int na long (literały całkowitoliczbowe domyślnie są typu int), kolejna zawiera jawne rzutowanie 123 na typ short, które następnie konwertowane jest niejawnie z powrotem na typ int. Ostatni przykład to konwersja z typu float do double.

Automatyczna konwersja podczas operacji arytmetycznych

Podczas operacji arytmetycznych także może dochodzić do niejawnej konwersji. Zgodnie ze specyfikacją języka Java (https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.2) możliwa jest konwersja (zachodzi pierwszy pasujący warunek):

  1. rozszerzająca do typu double jeśli którykolwiek z elementów operacji arytmetycznej jest typu double,
  2. rozszerzająca do typu float jeśli którykolwiek z elementów operacji jest typu float,
  3. rozszerzająca to typu long jeśli którykolwiek z elementów operacji jest typu long,
  4. rozszerzająca do typu int.

Wszystkie cztery przypadki pokazane są w przykładzie poniżej:

Tutaj drobna dygresja, operator dzielenia (/) wykonuje w języku Java dzielenie całkowitoliczbowe jeśli dzielna i dzielnik są całkowitoliczbowe. Jeśli chcemy otrzymać typ zmiennoprzecinkowy co najmniej jeden z elementów musi być typu zmiennoprzecinkowego:

Boxing i unboxing

Jak wiesz w języku Java występują zarówno typy proste jak i obiekty reprezentujące liczby np. int i Integer. Kompilator Java jest w stanie dokonać konwersji pomiędzy odpowiadającymi sobie typami prostymi i obiektami automatycznie. Proszę spójrz na przykład poniżej

Mamy tu do czynienia z tak zwanym „unboxing‚iem”, czyli automatyczym odpakowywaniem obiektu do odpowiadającego mu typu prostego.

Przykłady powyżej pokazują z kolei „boxing„, czyli automatyczne tworzenie instancji obiektów na podstawie typów prostych.

Podczas boxingu/unboxingu może dość do rzucenia różnych wyjątków, na przykład gdy obiekt przypisywany do typu prostego jest nullem lub gdy do typu prostego próboujemy przypisać inny obiekt (np. Long do int).

Konwersja do typu String

Konwersja do typu String jest specyficznym rodzajem konwersji automatycznej. Jest ona specyficzna ponieważ bazuje na metodzie toString, która może być przedefiniowana przez programistę. Konwersja do typu String zachodzi przy operatorze dodawania + jeśli któryś z dodawanych elementów jest typu String.

Typy proste także są automatycznie konwertowane do typu String, odbywa się to dwuetapowo, na początku zachodzi boxing następne obiekt konwertowany jest do typu String (wywoływana jest metoda toString).

Zadania

  1. Napisz program przyjmujący od użytkownika liczbę całkowitą i wyświetl wynik mnożenia tej liczby oraz stałej pi (Math.PI). Wyświetl wynik w postaci liczby całkowitej i liczby zmiennoprzecinkowej.
  2. Napisz program pobierający od użytkownika dwie liczby całkowite. Wyświetl wynik ich dzielenia wraz z częścią ułamkową.
  3. Napisz program, który skończy się wyjątkiem spowodowanym błędem podczas boxingu/unboxingu.
  4. Jak myślisz co otrzymasz przypisując zmienną typu char do zmiennej typu int? Znajdziesz ten numer w tabeli ASCII?

Przygotowałem też dla Ciebie zestaw przykładowych rozwiązań zadań, analizując je także możesz się czegoś nauczyć.

Dodatkowe materiały do nauki

Podsumowanie

Właśnie dowiedziałeś się o kilku kolejnych zakamarkach języka Java. Mam nadzieję, że Ci się podobało. Jak zwykle na koniec mam do Ciebie prośbę, podziel się artykułem ze swoimi znajomymi, zleży mi na dotarciu do jak największego grona czytelników. Jeśli nie chcesz pominąć żadnego postu polub Samouczka na facebooku. Do następnego razu! 🙂

Newsletter

  Jeśli chcesz otrzymywać informacje o nowych artykułach na blogu prosto na Twój email, zapisz się 🙂

Zdjęcie dzięki uprzejmości https://www.flickr.com/photos/parrhesiastes

  1. Wartość -1 wynika ze sposobu zapisywania liczb w Javie. Wiesz już o binarnym zapisie, tutaj wykorzystywana jest jego specyficzna odmiana – uzupełnienia do dwóch, jeżeli jesteś zainteresowany szczegółami daj znać w komentarzu, skrobnę o tym artykuł 🙂

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *