Literały a autoboxing
Pisząc swój pierwszy kod na pewno zastanawiałeś/aś się dlaczego klasy takie jak String, Integer, czy Double inicjalizuje się poprzez znak ‘=’. Przecież mówiłem w poprzedniej lekcji, że obiekty tworzy się poprzez słowo kluczowe ‘new’. Tak jest w rzeczy samej. Także obiekty wyżej wymienionych klas można stworzyć w ten sam sposób. Dlaczego więc istnieje opcja przypisywania poprzez znak równości, tak jak w przypadku zmiennych prostych? Nie ma tu żadnej pokrętnej logiki, jest to już cecha samego języka. Prawdopodobnie twórcy Java stwierdzili, że tak będzie po prostu łatwiej dla wielu programistów. Obiekty, które możemy stworzyć zarówno poprzez new jak i poprzez operator przypisania nazywa się literałami.
Przykłady literałów w Javie:
- String
- Char
- Byte
- Short
- Integer
- Long
- Float
- Double
- Boolean
Tak jak się pewnie domyślasz, są to obiektowe odpowiedniki wszystkich typów prostych (poza String, który nie ma typu prostego).
public class Autoboxing { public static void main(String [] args) { Integer mySalary = new Integer(10); int doubleSalary = 2 * mySalary; Integer salaryPlusBonus = doubleSalary + 500; int intValue = salaryPlusBonus.intValue(); Integer integerValue = new Integer(intValue); } }
Czym jest autoboxing?
W przykładzie wyżej widać , że typy proste i ich odpowiedniki można łatwo przekształcić w siebie nawzajem. Poza łatwym zapisem takie rozwiązanie daje jeszcze jedną zaletę. Czasami potrzebujemy liczbę całkowitą w formie obiektowej. Wtedy z pomocą przychodzą klasy osłonowe. Oba typy można używać zamiennie, ponieważ kompilator wykonuje automatycznie konwersje z jednego na drugi.
Pojawia się tez jeszcze jeden problem, który dokładnie omówię przy lekcji związanej z dziedziczeniem i superklasą o nazwie ‘Object’. Mianowicie, ponieważ przypisać literał można na dwa sposoby (z użyciem new lub ‘=’, wykorzystanie standardowego operatora porównania (‘==’) nie jest wystarczający.
Rozważ następujący problem:
public class StringEquals { public static void main(String[] args) { String exampleInit = "My Text"; String exampleNew = new String("My Text"); if (exampleInit == exampleNew) { System.out.println("works by equals character"); } if (exampleInit.equals(exampleNew)) { System.out.println("works by equals key word"); } } }
Jeśli poprawnie przepisałeś/aś kod, to w takim razie na konsoli wyświetli się napis works by equals. Dzieje się tak dlatego, że jeden String jest literałem a drugi zwykłym obiektem a obiektów, poprzez ich złożoność, nie da się porównać zwykłym znakiem ‘==’. O metodzie ‘equals’, do czego służy i skąd się wzięła dowiesz się później. Zapamiętaj jedynie, że pomimo, że typy proste i ich obiekty opakowujące wyglądają podobnie, to nie są dokładnie tym samym.
Dlaczego, więc w ogóle istnieją typy proste i ich obiektowe odpowiedniki?
Otóż dawno temu typy proste wystarczały w wielu sytuacjach do programowania (np. gdy potrzebowaliśmy liczb całkowitych lub ułamkowych). Od Javy w wersji 5 powstały specjalne typy generyczne, które umożliwiają na przykład wygodną pracę z kolekcjami (np. z tablicą dynamiczną ArrayList). Niestety ich wadą jest używanie jedynie obiektów wewnątrz siebie, a nie typów prostych. Dlatego też stworzono obiektową wersję każdego typu prostego.
Jeśli chcesz możesz śmiało zawsze używać klas osłonowych zamiast prymitywów, istnieje jednak jeden powód, dla którego warto rozważyć deklaracje zmiennej np. int czy float. Jest nim optymalizacja pamięci, każdy obiekt poza danymi ‘waży’ też swoją referencję. Poza tym klasy osłonowe takie jak Integer posiadają w sobie dodatkowe implementacje. W praktyce jednak poza skrajnymi przypadkami nie ma to dużego znaczenia czy użyjesz typu prostego czy jego wersji obiektowej.
Link do lekcji na githubie: https://github.com/developeronthego/java-basics/tree/main/src/main/java/basics/lesson3
Link do lekcji o typach prostych: https://developeronthego.pl/java-podstawy-1-typy-danych/