Ostatnio omawialiśmy rozwój klasy String w Javie od wersji 8 do 15. Wspominaliśmy tam, że w Javie 15 zostały dodane 3 nowe metody do klasy String. Oczywiście to nie są wszystkie nowości w tej wersji Javy. Dzisiaj przyjrzymy się jakie inne nowości możemy używać w Java od wersji 15.

Algorytm podpisu cyfrowego

Edwards-Curve Digital Signature Algorithm

Pierwszą zmianą, opisaną po numerem JEP 339, jest nowy algorytm podpisu cyfrowego oparty na krzywych eliptycznych. Jego oryginalna nazwa to Edwards-Curve Digital Signature Algorithm, a w skrócie EdDSA.

Poniżej zobaczmy jak możemy wykorzystać nowy algorytm w naszym kodzie i przykładowy wynik.

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EdDSA");
KeyPair keyPair = keyPairGenerator.generateKeyPair();

PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();

System.out.println(Arrays.toString(publicKey.getEncoded()));
System.out.println(Arrays.toString(privateKey.getEncoded()));
[48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 53, -105, 68, 17, -24, 121, -109, 99, -12, 119, 6, -41, -15, -70, 86, -111, -74, 107, -67, 79, -61, 0, 87, 118, -35, -95, -9, 108, 87, -123, -61, 54]
[48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 70, -23, -62, -45, -124, 28, 89, 127, 51, -71, -17, -31, -26, 48, 82, -100, -47, 23, -48, 103, 123, 49, 90, 66, 43, -116, -28, 15, 42, 121, -1, -119]

Przy każdym uruchomieniu powyższego kodu oczywiście jest generowana nowa para kluczy.

Zapieczętowane klasy

Sealed Classes

Kolejną nowością są Sealed Classes, które są opisane pod JEP 360. Jest to feature oznaczony jako Preview, oznacza to, że ostateczna wersja tej funkcjonalności może jeszcze ulec zmianie.

Standardowo, kiedy tworzymy klasy, których nie oznaczamy jako final to mogą być dziedziczone przez dowolną inną klasę (zakładając, że modyfikator dostępu pozwala na dostęp do niej).

Mechanizm Sealed Classes pozwala na stworzenie zamkniętej hierarchii klas, czyli takiej, która nie będzie mogła być rozbudowana. Może to być użyteczne, kiedy chcemy stworzyć hierarchie klas, która nie powinna być rozszerzana np. w ramach jednego modułu.

Sprawdźmy zatem w jaki sposób możemy zaimplementować naszą ‘zapieczętowaną’ hierarchie.

Na początku musimy zadeklarować naszą klasę bazową wraz z wszystkimi klasami, które po niej bezpośrednio dziedziczą.

sealed class Vehicle
      permits Bike, Car, Boat {
}

W powyższym kodzie pojawiają się dwa nowe słowa kluczowe sealed i permits. Pierwszym słowem oznaczamy, że nasza klasa będzie ‘zapieczętowana’ czyli dziedziczona tylko przez określone klasy. Po drugim słowie kluczowym musimy wylistować wszystkie dziedziczące klasy.

Teraz zaimplementujmy nasze 3 klasy, które zdefiniowaliśmy wcześniej.

final class Bike extends Vehicle {}
non-sealed class Boat extends Vehicle {}
sealed class Car extends Vehicle
      permits Audi {}

To co możemy od razu zauważyć to 3 różne sposoby jak klasy są zdefiniowane. Klasy dziedziczące po ‘zapieczętowanej’ klasie muszą mieć jedno z poniższych słów kluczowych w swojej definicji:

  • final – oznaczamy w ten sposób, że po klasie nie będzie można dziedziczyć,
  • non-sealed – oznaczamy w ten sposób, że po klasie będzie można dziedziczyć, czyli w przypadku normalnej klasy jest to odpowiednik klasy bez słowa kluczowego final,
  • sealed – w ten sposób rozpoczynamy nową hierarchie klas czyli również musimy zdefiniować klasy dziedziczące po słowie permits.

Jeszcze na koniec spróbujmy stworzyć klasę, która dziedziczy po klasie Vehicle, ale nie została zdefiniowana w hierachii.

class Train extends Vehicle {}

Przy próbie kompilacji powyższego kodu otrzymamy następujące błąd:

java: class is not allowed to extend sealed class: Vehicle

Ukryte klasy

Hidden Classes

Następną nową funkcjonalnością, o której sobie powiemy są Hidden Classes, o których możemy poczytać w tym dokumencie: JEP 371.

Mechanizm ten, pozwala nam zdefiniować klasy, które nie będą mogły być użyte przez bytecode innych klas. Ideą, która stoi za ukrytymi klasami jest umożliwienie framework-om czy językom na JVM generowanie klas w czasie wykonania programu (Runtime) i używanie ich przez refleksje.

Usunięcie silnika JavaScriptu

Nashorn

Kolejną zmianą, o której można poczytać tutaj, jest usunięcie silnika JavaScriptu – Nashorn, który był oznaczony jako deprecated, od Javy 11.

Wyłączenie Biased Locking

Biased Locking

Biased Locking jest pewną optymalizacją, która występuje przy synchronizacji. JVM jest w stanie wykryć czy synchronizowany obiekt jest używany tylko przez jeden wątek. Jeżeli tak, to JVM jest w stanie wykonać kolejne operacje na tym obiekcie bez ponoszenia kosztów związanych z synchronizacją.

Do Javy 15 mechanizm ten był domyślnie włączony. Od wersji 15 został on wyłączony, a dodatkowo sam mechanizm został oznaczony jako deprecated.

Więcej informacji na ten temat możemy znaleźć w JEP 374.

Dopasowanie wzorca dla instanceof

Pattern Matching for instanceof

Kolejna zmiana w Java 15, która jest opisana w JEP 375, to Pattern Matching for instanceof. Funkcjonalność pojawiła się poraz pierwszy w Javie 14 i była oznaczona jako Preview. Obecnie funkcjonalność oznaczona jest jako Second Preview.

Tą funkcjonalność najłatwiej będzie wytłumaczyć na przykładzie, zatem od razu do niego przechodzimy 🙂

Załóżmy, że chcemy stworzyć metodę, która przyjmuje jeden parametr i zwraca jego rozmiar. Haczyk tkwi w parametrze, który może być zarówno Stringiem jak i listą.

Napiszmy więc taką metodę bez używania nowego mechanizmu.

static int getSize(Object obj) {
   if (obj instanceof String) {
      return ((String) obj).length();
   } else if (obj instanceof List) {
      return ((List<?>) obj).size();
   }
   return 0;
}

Ponieważ chcemy, żeby nasza metoda była uniwersalna to musimy przyjąć Object jako parametr. Następnie dla każdego obsługiwanego typu robimy sprawdzenie typu używając instanceof, a następnie robimy jawne rzutowanie na dany typ, na którym wywołujemy metodę odpowiedzialną za wyliczenie rozmiaru dla tego typu, czyli np. String.length() czy List.size().

Teraz pora na podobną metodę, ale używając mechanizmu Pattern Matching.

static int getSize(Object obj) {
   if (obj instanceof String str) {
      return str.length();
   } else if (obj instanceof List<?> list) {
      return list.size();
   }
   return 0;
}

Tutaj podobnie jak poprzednio przyjmujemy Object jako parametr, a następnie możemy zauważyć użycie nowego mechanizmu. Przy sprawdzaniu typu obiektu przy pomocy instanceof, po podaniu typu, do którego porównujemy (np. String czy List w naszym przykładzie) podajemy nazwę zmiennej, pod którą będzie dostępny nasz parametr rzutowany już na dany typ. Dzięki temu w danym bloku if’a możemy używać nowej zmiennej i wywoływać na niej metody odpowiednie dla danego typu bez konieczności robienia jawnego rzutowania.

Nowy algorytm Garbage Collector

Z Garbage Collector

Kolejną nowością, która jest opisana po numerem JEP 377, jest nowy algorytm Garbage Collectora nazwany Z Garbage Collector. Nowy mechanizm został wprowadzony już w Javie 11, ale jako funkcjonalność eksperymentalna.

Głównymi cechami nowego algorytmu mają być małe opóźnienia (max 10ms) przy jednoczesnej obsłudze Stosów (Heap) o rozmiarach mierzonych w terabajtach.

Bloki Tekstowe

Text Blocks

Następną funkcjonalnością są Bloki Tekstowe (Text Blocks) oznaczone JEP 378. Funkcjonalność została wprowadzona w Java 13 (Preview), a następnie w Java 14 jako Second Preview.

Bloki tekstowe pozwalają nam stworzyć wielolinijkowe Stringi w wygodny sposób. Poniżej sprawdźmy przykład takiego bloku oraz wynik tego fragmentu kodu.

String text = """
Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
Donec rutrum ac lectus sed rhoncus.
""";
System.out.println(text);
Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
Donec rutrum ac lectus sed rhoncus.

Kolejny Garbage Collector

Shenandoah

I po raz kolejny w zestawieniu mamy algorytm Garbage Collector. Jest to finalna wersja algorytmu, którego pierwsza wersja została wprowadzona w Java 11. Algorytm Shenandoah został opisany pod numerem JEP 379.

Algorytm działa na podobnej zasadzie jak G1, z tym że wykonuje operacje kompaktowania również w sposób współbieżny.

Usunięcie SOLARISa

SOLARIS

JEP 381 to oznaczenie kolejnej zmiany w Java 15, którą jest usunięcie kodu związanego z systemem operacyjnym SOLARIS oraz architekturą SPARC.

API do pamięci zewnętrznej

Foreign-Memory Access API

Kolejną nowością oznaczoną jako JEP 383 jest API dostępu do pamięci zewnętrznej (Foreign-Memory Access API). Feature jest oznaczony jako Second Incubator, czyli jest to funkcjonalność, która jeszcze nie jest w pełni skończona, może być zmieniona czy nawet usunięta w przyszłych wersjach Javy.

Nowe API dostarcza bezpieczny i wydajny sposób dostępu do pamięci, zarówno Heapa jak i pamięci natywnej.

Rekordy

Records

Kolejna, przedostatnia już nowość, to Rekordy (Records) oznaczone numerem JEP 384. Feature jest oznaczony jako Second Preview czyli mogą w nim zostać wprowadzone jeszcze jakieś zmiany, ale nie powinniśmy spodziewać się rewolucji 🙂

Rekordy są mechanizmem, który pozwala nam stworzyć Data Class, czyli klasę która zawiera pola wraz metoda dostępowymi do nich, implementacje metod equals, hashCode oraz toString. Jeżeli używaliśmy wcześnie biblioteki lombok to mogliśmy to osiągnąć przez użycie adnotacji @Data na klasie.

Spójrzmy zatem na przykładową implementacje rekordu oraz jego użycia.

record Point(int x, int y) {}
Point p = new Point(1, 3);

System.out.println("p.x() = " + p.x());
System.out.println("p.y() = " + p.y());
System.out.println("p = " + p);
p.x() = 1
p.y() = 3
p = Point[x=1, y=3]

Jak widzimy implementacja Rekordu polega na użyciu słowa kluczowe records zamiast class oraz zdefiniowaniu pól w nagłówku klasy.

Oznaczenie RMI Activation jako do usunięcia

Ostatnią zmianą, która następuję w Java 15, jest oznaczenie mechanizmu RMI Activation jako deprecated z informacją, że będzie usunięty w przyszłości. Zmiana ta jest oznaczona numerem JEP 385.

Podsumowanie

Dzisiaj przyjrzeliśmy się wszystkim zmianom i nowością w Java 15. Część z nich prawdodpobnie nie wpłynie na naszą codzienną pracę, lecz z pewnością funkcjonalności takie jak Rekordy, Bloki Tekstowe czy Dopasowanie Wzorca dla instanceof mogą ułatwić nasze codzienne obcowanie z nową wersją Javy 🙂