Java 18
Spis treści
- W skrócie
- Java 18 w kontekście innych wersji
- Przegląd zmian
- JEP 400: UTF-8 by Default
- JEP 408: Simple Web Server
- JEP 413: Code Snippets in Java API Documentation
- JEP 416: Reimplement Core Reflection with Method Handles
- JEP 417: Vector API (Third Incubator)
- JEP 418: Internet-Address Resolution SPI
- JEP 419: Foreign Function & Memory API (Second Incubator)
- JEP 420: Pattern Matching for switch (Second Preview)
- JEP 421: Deprecate Finalization for Removal
- Podsumowanie
W skrócie
Java 18 ustanawia kodowanie UTF-8 jako domyślne, wprowadza prosty serwer webowy oraz poprawia dodawanie fragmentów kodu do dokumentacji. Nadal trwają prace nad dopasowywaniem wzorców w switchu, a metoda finalize() została oznaczona jako "do usunięcia".
Java 18 w kontekście innych wersji
Java 18 została wydana 22 marca 2022 roku. Jest to wydanie z krótkim, półrocznym okresem wsparcia, aż do wydania Java 19. Warto wspomnieć, że poprzednia wersja LTS (Long Time Support) to Java 17, a kolejna to Java 21.
Przegląd zmian
Zmiany wprowadzone w tej wersji są raczej zakulisowe i nie możemy się tutaj spodziewać nowych składni języka.
JEP 400: UTF-8 by Default
Do tej pory kodowanie znaków zależne było od systemu operacyjnego, na którym uruchamiany był program. Plik o nazwie "hello.txt" o zawartości "こんにちは" zapisany na macOS był odczytywany w następujący sposób:
java.io.FileReader("hello.txt") -> "こんにちは" (macOS)
java.io.FileReader("hello.txt") -> "ã?"ã‚"ã?<<ã?¡ã? " (Windows (en-US))
java.io.FileReader("hello.txt") -> "縺ォ縺。縺ッ" (Windows (ja-JP)
Dało się temu zapobiec, ustawiając dodatkowy parametr -Dfile.encoding=UTF8, więc większość projektów właśnie tak sobie z tym radziła. Od Java 18 nie będzie potrzeby ustawiania tej wartości, bo UTF-8 będzie domyślnym kodowaniem. Jeśli jakiś projekt polegał na poprzednim podejściu do kodowania znaków, to może ustawić -Dfile.encoding=COMPAT w celu zachowania dotychczasowej funkcjonalności.
JEP 408: Simple Web Server
Wprowadzono prosty serwer webowy do hostowania plików. Mocnym założeniem jest to, żeby nie było w nim za dużo funkcjonalności, bo to jest już realizowane przez inne znakomite rozwiązania: Jetty, Netty, Grizzly, Apache Tomcat, Apache httpd, Nginx. Tutaj chodzi o to, żeby dało się go wystartować jednym poleceniem.
Po co komu taki serwer? Głównie chodzi o sytuacje związane z edukacją, prototypowaniem czy testowaniem. Wystarczy wpisać polecenie jwebserver do cmd i serwer zostanie uruchomiony.
JEP 413: Code Snippets in Java API Documentation
Do tej pory przedstawianie fragmentów kodu w dokumentacji było realizowane za pomocą @code i wyglądało następująco:
<pre>{@code
Optional<Path> p =
uris.stream().filter(uri -> !isProcessedYet(uri))
.findFirst()
.map(Paths::get);
}</pre>
Powyższy fragment to część dokumentacji metody Optional.map, wygenerowaną na tej podstawie dokumentację znajdziesz tutaj.
Takie rozwiązanie ma kilka problemów:
- Narzędzia nie mają dobrego sposobu na znajdowanie i przez to walidację składni w takich fragmentach
- Nie da się utworzyć niezawodnego mechanizmu kolorowania składni dla takich fragmentów
- Brak wsparcia IDE do tworzenia takich fragmentów, przez co jest brak podpowiadania składni
- Nie można w nich używać składni HTML
- Takie fragmenty nie mogą zawierać komentarzy dokumentujących
- Są problemy ze wcięciami, są one zależne od początku komentarza
Rozwiązaniem ma być wprowadzenie @snippet, który ma wyglądać następująco:
/**
* The following code shows how to use {@code Optional.isPresent}:
* {@snippet :
* if (v.isPresent()) {
* System.out.println("v: " + v.get());
* }
* }
*/
Co ciekawe, takie snippety będą mogły również linkować do zewnętrznych plików.
/**
* The following code shows how to use {@code Optional.isPresent}:
* {@snippet file="ShowOptional.java" region="example"}
*/
Gdzie zawartość ShowOptional.java to:
public class ShowOptional {
void show(Optional<String> v) {
// @start region="example"
if (v.isPresent()) {
System.out.println("v: " + v.get());
}
// @end
}
}
JEP 416: Reimplement Core Reflection with Method Handles
Nie ma to wpływu na API mechanizmu refleksji. Celem zadania jest ujednolicenie wewnętrznej implementacji, bo do tej pory istniały 3 sposoby na realizację tych samych funkcjonalności. Projekt Valhalla zakłada zmiany dotykające bytecode, który powoduje, że każda zmiana w tym module zostać wykonana trzykrotnie, co wprowadzało niepotrzebne koszty.
JEP 417: Vector API (Third Incubator)
Dalsza część prac nad API do wyrażania obliczeń na wektorach. Celem jest wprowadzenie spójnego interfejsu do pracy na wektorach, które pozwoli maksymalnie wykorzystać wsparcie sprzętowe. Poniżej film omawiający to zagadnienie.
JEP 418: Internet-Address Resolution SPI
Aktualnie Java korzysta z systemowych sposobów na pobieranie informacji o nazwie hosta (ang. hostname). Jest to problematyczne, bo zapytanie o nazwę hosta jest blokujący i ciężko to połączyć z wirtualnymi wątkami, które ma zamiar wprowadzić Projekt Loom. Dodatkowe motywacje to:
- Wsparcie dla nowych sposobów pozyskiwania adresów, np. DNS over QUIC, TLS, czy HTTPS
- Możliwość lepszego dostosowywania pobierania nazwy hosta do aktualnego przypadku (pierwsze co mi przychodzi do głowy to whitelista/blacklista niektórych domen)
- Testowanie --- będzie możliwość mockowania pozyskiwania adresów
Z tego co udało mi się znaleźć to został wprowadzony nowy interfejs InetAddressResolver, posiada on aktualnie 2 implementacje:
- java.net.InetAddress.PlatformResolver
- java.net.InetAddress.HostsFileResolver
Obie istnieją już od Java 9, ale teraz można zarejestrować własną implementację korzystając z InetAddressResolverProvider.
JEP 419: Foreign Function & Memory API (Second Incubator)
To zadanie ma na celu zastąpienie JNI, leciwego już sposobu uruchamiania natywnych metod. Ma to być zupełnie nowe API, a nie zmiany w JNI. Ogólnie będzie wygodniej, szybciej i lepiej.
JEP 420: Pattern Matching for switch (Second Preview)
Część projektu Amber. Jest to drugi, ale jeszcze nieostateczny (dla JDK 19 zaplanowany jest trzeci) podgląd dopasowywania do szablonu w switchu. Weźmy na przykład taki kawałek kodu:
record Point(int i, int j) {}
enum Color { RED, GREEN, BLUE; }
static void typeTester(Object o) {
switch (o) {
case null -> System.out.println("null");
case String s -> System.out.println("String");
case Color c -> System.out.println("Color with " + c.values().length + " values");
case Point p -> System.out.println("Record class: " + p.toString());
case int[] ia -> System.out.println("Array of ints of length" + ia.length);
default -> System.out.println("Something else");
}
}
Tutaj obiekt o może być dopasowany typu klasy, typu enuma, typu tablicy, a oprócz tego do null czy default. Nadal trwają prace nad tym mechanizmem. Rozważane są przypadki brzegowe, sytuacje gdzie obiekt pasuje do kilku wzorców, albo gdzie nie wszystkie możliwości są objęte switchem. Zainteresowanym polecam poczytać szczegóły. Link poniżej.
JEP 421: Deprecate Finalization for Removal
Przygotowanie gruntu pod usunięcie metody Object.finalize() z języka. Co ciekawe ta metoda została oznaczona jako deprecated już a Java 9, natomiast teraz zyskała dodatkową flagę forRemoval=true. Przypomnę tylko, że ta metod jest automatycznie wywoływana przez Garbage Collector na chwilę przed usunięciem obiektu z pamięci. Została wprowadzona na samym początku języka i jest to niejako pozostałość po C / C++ gdzie zwalnia się zasoby manualnie. W Javie nie pamięcią zarządza Wirtualna Maszyna, więc nie ma potrzeby zwalniania zasobów samodzielnie.
Wraz z tą zmianą wprowadzono:
- Dodatkowy parametr (--finalization=disabled), który pozwala na wyłączenie tego mechanizmu (metoda finalize() nie będzie wywoływana przez JVM). Uwaga! Uruchomienie tej flagi może powodować nieprzewidywalne zachowanie (bo finalize() jest używana również w ramach JDK), więc należy używać tylko na środowiskach testowych
- Nowy zestaw statystyk dla JDK Flight Recorder (JFR), zbierające dane o finalizacji podczas działania programu
Następujące metody zostały oznaczone @Deprecated(forRemoval=true:
- java.lang.Object.finalize()
- java.lang.Enum.finalize()
- java.awt.Graphics.finalize()
- java.awt.PrintJob.finalize()
- java.util.concurrent.ThreadPoolExecutor.finalize()
- javax.imageio.spi.ServiceRegistry.finalize()
- javax.imageio.stream.FileCacheImageInputStream.finalize()
- javax.imageio.stream.FileImageInputStream.finalize()
- javax.imageio.stream.FileImageOutputStream.finalize()
- javax.imageio.stream.ImageInputStreamImpl.finalize()
- javax.imageio.stream.MemoryCacheImageInputStream.finalize()
Podsumowanie
Pomimo braku nowych funkcjonalności, to widać, że Java nadal się stabilnie rozwija. Konsekwentnie rozwijane są nowe funkcjonalności i pokazywane społeczności do testów. Moim zdaniem dobrze, że nie ma pochopnych i impulsywnych decyzji, które powodowałby wprowadzenie nieprzemyślanych funkcji do języka.
Dyskusja i komentarze
Masz pytania do tego wpisu? Może chcesz się podzielić spostrzeżeniami? Zapraszamy dyskusji na naszej grupie na Facebooku.