Nowa wersja Javy zostanie wydana już niedługo bo została wydana 17 marca. Java 14 to całkiem spora lista nowości. W tej wersji znalazło się aż 16 JEPów (JDK Enhancement Proposal). Dwa z nich debiutowały w poprzednich wersjach Java 13 i Java 12, ale większość jest zupełnie nowa.
Wszystkie nowości uszeregowałem od najbardziej istotnej do najmniej istotnej (moim zdaniem oczywiście).
Records (Preview)
Najważniejszą funkcją jaka została dodane w tej wersji jest nowy typ klasy Record
. Klas ta powstała w celu zmniejszenia ilości kodu generowanego przez programistę (boilerplate code), takiego jak: gettery, konstruktor, equals, hascode i toString.
import java.util.Objects; public class User { public String name; public String email; public User(String name, String email) { this.name = name; this.email = email; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(name, user.name) && Objects.equals(email, user.email); } @Override public int hashCode() { return Objects.hash(name, email); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", email='" + email + '\'' + '}'; } }
Taką klasę która ma prawie 50 linii (gdzie są tylko dwa pola: name i email) możemy zastąpić jedną linijką:
public record User(String name, String email) {}
Podobne konstrukcje można już od dawna spotkać w innych językach programowania np.: Kotlin ma Data Classes, Scala ma Case Classes.
Żeby dostać się do pól takiego obiektu używamy metod: user.name()
i user.email()
. Nie ma tutaj charakterystycznego get
. Ale zmiana ta została udostępniona jako preview, więc nie wykluczone, że get
zostanie dodany do metod w kolejnej wersji ?
Obiekty, które powstają z rekordów są niezmienne (immutable). O obiektach niezmiennych pisałem w Pytania rekrutacyjne Java – Obiekty niezmienne (immutable).
Poza tym rekordy nie mogą dziedziczyć po innych klasach. Rekordy to także klasy final
, więc też nie można po nich dziedziczyć. A wszystkie pola w klasie rekord są zawsze prywatne i finalne.
Żeby włączyć tą funkcjonalność trzeba dodać odpowiednie parametry do kompilatora:
javac --enable-preview --release 14 User.java
Linki: JEP 359
Rekordy w Javie szczegółowo omówiłem w poniższym filmie:
Pomocne komunikaty dla NullPointerException (Helpful NullPointerExceptions)
Modyfikacja ta pozwala pokazać bardziej pomocne wiadomości dla NullPointerException. Zwłaszcza jeśli wywołujemy wiele metod kolejno w taki oto sposób: obj.metod1().method2().method3()
. W takim wypadku NullPointerException może wystąpić w trzech miejscach. Ze standardowego komunikatu nie da się wywnioskować, w którym miejscu tej linijki powstał błąd. Mamy podany tylko numer linii i to wszystko.
W Javie 14 jest dużo lepiej. Dla przykładowego kodu:
public class HelpfulNullPointerException { private Student person; public Student getStudent() { return person; } public static void main(String[] args) { HelpfulNullPointerException obj = new HelpfulNullPointerException(); System.out.println(obj.getStudent().name()); } }
gdzie klasa Student to rekord:
record Student (String name) {}
otrzymujemy wyjątek
Exception in thread "main" java.lang.NullPointerException at HelpfulNullPointerException.main(HelpfulNullPointerException.java:10)
Żeby włączyć rozszerzone komunikaty musimy ustawić JVM odpowiednią flagę:
-XX:+ShowCodeDetailsInExceptionMessages
wtedy otrzymujemy komunikat:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Student.name()" because the return value of "HelpfulNullPointerException.getStudent()" is null at HelpfulNullPointerException.main(HelpfulNullPointerException.java:10)
Linki: JEP 358
Text Blocks (Second Preview)
Bloki tekstowe weszły jako zapowiedź w Javie 13. Pisałem o tym w poprzednim zestawieniu: Java 13 przegląd nowości. W obecnej wersji dodano dwa specjalne znaki „\”, który zapobiega wstawianiu znaku końca linii. I drugi „\s” który zapobiega obcinaniu spacji na końcach linii.
Linki: JEP 368
Switch Expressions (Standard)
Switch Expressions ogłoszone zostało jako zapowiedź (preview) w Javie 12 (Java 12 nowości). W obecnej wersji wchodzi już do standardu.
Można go używać jako instrukcję warunkową (tak samo jak było do tej pory) jak i wyrażenie:
String label = switch (number) { case 0 -> "zero"; case 1 -> "one"; case 2 -> "two"; default -> "many"; }
Linki: JEP 361
Pattern Matching for instanceof (Preview)
Do tej pory używanie instanceof
wiązało się z deklaracją zmienne wewnątrz warunku w którym sprawdzaliśmy czy dany obiekt jest jakiegoś typu. Mimo, że obiekt został już sprawdzony musieliśmy dokonać rzutowania na dany typ:
if (obj instanceof String) { String string = (String) obj; System.out.println(string); }
W nowej wersji wprowadzono konstrukcję, która pozwoli trochę uprościć ten zapis
if (obj instanceof String string) { System.out.println(string); } else { // nie możesz użyć tutaj zmiennej string }
Linki: JEP 305
Packaging Tool (Incubator)
JEP ten ma za zadanie wprowadzić do Javy narzędzie służące do budowania pakietów instalacyjnych specyficznych dla danego systemu operacyjnego. Będzie obsługiwał systemy:
- Linux:
deb
andrpm
- macOS:
pkg
anddmg
- Windows:
msi
andexe
Narzędzie jpackage
będzie pakowało wszystkie potrzebne zależności (jary) do jednego pakietu instalacyjnego.
Przykładowe uruchomienie:
$ jpackage --name myapp --input lib --main-jar main.jar
Linki: JEP 343
Foreign-Memory Access API (Incubator)
Jest to nowe API, które ma zastąpić takie klasy jak sun.misc.Unsafe
i java.nio.ByteBuffer
. Przede wszystkim ma być bezpieczne (i też wydajnie). Czego nie gwarantują klasa Unsafe
. Użycie jej może być mało bezpieczne i może prowadzić do nagłego zakończenia procesu. Z kolei ByteBuffer może być trochę mniej wydajny niż Unsafe
.
Linki: JEP 370
JFR Event Streaming
JEP ten ma za zadanie umożliwić strumieniowanie zdarzeń JDK Flight Recorder. Ma to po prawić możliwości monitorowania aplikacji zarówno na zewnątrz jak i wewnątrz. I to wszystko przy założeniu, że narzut wydajnościowy będzie mniejszy niż 1%.
Linki: JEP 349
Non-Volatile Mapped Byte Buffers
Dodano nowy tryb mapowania plików. Jest to usprawnienie dla MappedByteBufferu pozwalające na dostęp do pamięci nieulotnej (NVM).
Linki: JEP 352
NUMA-Aware Memory Allocation for G1
NUMA (non-uniform memory access) jest to ulepszenie poprawiające wydajność Garbage Collectora G1 działającego na dużych wieloprocesorowych maszynach.
Linki: JEP 345
ZGC on macOS i ZGC on Windows
Z Grarbage Collector został przeportowany na platformy macOS i Windows. Został on wprowadzony w Javie 11 (JEP 333) jako GC z bardzo niskimi pauzami i wtedy wspierany był tylko na platformie Linux.
Remove the Concurrent Mark Sweep (CMS) Garbage Collector
Został usunięty Garbage Collector Concurrent Mark Sweep. Wcześniej został on oznaczony jako przestarzały w Javie 9.
Linki: JEP 363
Deprecate the ParallelScavenge + SerialOld GC Combination
Jako przestarzała została oznaczona kombinacja algorytmów odśmiecania Parallel Scavenge i Serial Old.
Linki: JEP 366
Remove the Pack200 Tools and API
Zostaną usunięte narzędzia pack200
i unpack200
, które służyły do kompresowania archiwów jar.
Linki: JEP 367
Deprecate the Solaris and SPARC Ports
Porty Solaris/SPARC, Solaris/x64 i Linux/SPARC zostały oznaczone jako przestarzałe. Zostaną usunięte w kolejnych wersjach.
Linki: JEP 362
Podsumowanie
Z każdą wersją mamy coraz więcej drobnych zmian w Javie, które pomagają developerom w codziennej pracy. Zwłaszcza wprowadzona w tej wersji klasa rekord może znacząco zmniejszyć boilerplate code. Java powoli dogania inne nowsze języki i nie ma przy tym wielu problemów, które w tych szybko rozwijających się językach są (chociażby problem kompatybilności wstecznej).
Chyba zacznę migrować swoje projekty do czternastki ?
Źródła:
https://openjdk.java.net/projects/jdk/14/
Z sekcji „Pomocne komunikaty dla NullPointerException”
skoro klasa Student to rekord, a nieco wyżesz jest napisane że jej pola nie posiadają charakterystycznego „get” to skąd: obj.getStudent().getName() ?
Masz rację mały błąd. Najpierw to była normalna klasa. Dopiero później stwierdziłem, że mogę zrobić z tego rekord i zapomniałem przy tym poprawić kod. Poprawione.
Dzięki za wpis!