Spis treści

Java 18: co nowego? - przegląd JEP-ów

Wydanie Javy 18 planowane jest na 2021-03-22. Spójrzmy, co nowego się w nim (prawdopodobnie) znajdzie. Zapraszam na szybki przegląd planowanych zmian i nowości. Zaparzcie sobie kubek pysznek kawy ☕: i zaczynamy!

Gdzie śledzić JDK Enhancement Proposals (JEPS) dla Javy 18

Najlepszym miejscem do śledzenia tego, jakie zmiany będą włączone do Javy, jest strona openjdk.java.net podsumowująca określone wydanie.

Dla Javy 18 będzie to strona https://openjdk.java.net/projects/jdk/18/

Wciąż jescze trwa proces migracji wielu projektów na Javę 17.

Programiści, którzy (często boleśnie) przekonali się, że nie warto pozostawać z tyłu, starają się nadganiać dość jednak wysokie - jak na standardy firmowe - tempo, w jakim wydawane są kolejne wersje Javy.

Czasami nadganianie jest jedynie mentalne - jak w przypadku tych nieszczęśników, którzy wciąż używają Javy 7… wiem, wiem: takie mamy teraz uwarunkowania.

W tym wpisie przedstawię niektóre JEPy planowane w wydaniu 18 platformy openjdk.

Data “powszechnej dostępności” (general availability) dla JDK 18 została ustalona na 2022-03-22, więc całkiem możliwe, że lista JEPów, które ostatecznie wylądują w tym wydaniu, będzie się od poniższej listy znacznie różnić.

Według dzisiejszych planów istnieje osiem JEPów planowanych w Javie 18:

Przyjrzyjmy się niektórym z nich trochę bliżej

JEP - notatki czytelinka

Opisy JEP-ów, które znajdziecie poniżej, są jedynie moimi prywatnymi notatkami. Same propozycj (JEPy) będą prawdopodobnie zmieniać się w kolejnych wersjach dokumentów, podobnie jak prezentowane niżej API.

Niektóre zagadnienia, o których piszę poniżej, rozumiem jedynie częściowo, więc mogę w ich opisie popełnić merytoryczne błędy, za które potencjalnych czytelników z góry przepraszam.

Mam nadzieję, że - mimo możliwych błędów - ten artykuł stanie się inspiracją i zachętą do własnych poszukiwań.

Potrzeba ustalonego kodowania

400: utf-8 by default

Ten JEP został stworzony już cztery lata temu. Jego cel to poprawa przewidywalności programów napisanych w Javie w związku z użyciem zestawów znaków.

W przypadkach, gdy nie ma możliwości podania kodowania jako parametru wywołania funkcji z API, wówczas powinno być jednoznacznie ustalone, jakie kodowanie będzie użyte.

Na przykład, odniesienie do metody lub konstruktora w strumieniach lambda (::) możliwe jest tylko wtedy, gdy metoda/konstruktor są jednoargumentowe, więc w takich przypadkach programista nie może użyc standardowego parametru charset, choćby nawet istniała taka wersja metody/konstruktora.

Obecnie stosowane kodowanie jest zależne od platformy.

Według tego JEP-a, jeśli zawsze będzie używane ustalone z góry kodowanie, to będzie można w końcu pisać przenośny kod (między systemami mającymi różne kodowania domyślne), a dzięki temu osiągnąć przewidywalny rezultat (np. przetwarzania plików).

UTF-8 jako kodowanie domyślne

Wybrane zostało kodowanie UTF-8. Jest szeroko rozpowszechnione w sieci, jest używane w standardzie XML oraz w porządnych systemach operacyjnych takich jak Linux. Będzie więc domyślnie używane również w całym API biblioteki standardowej.

Domyślne kodowanie bieżącego JDK można sprawdzić używając parametru -XshowSettings:properties przy opcji java -version i szukając właściwości file.encoding:

1
java -XshowSettings:properties -version 2>&1 | grep file.encoding

Obecnie istnieje wiele API które używają kodowania systemowego:

  • w pakiecie java.io: konstruktory InputStreamReader, FileReader, OutputStreamWriter, FileWriter, i PrintStream
  • w pakiecie java.util: Formatter i Scanner
  • w pakiecie java.net: URLEncoder i URLDecoder (w metodach oznaczonych jako deprecated)

Zmiana

Najistotniejsza zmiana jest następująca: specyfikacja Charset.defaultCharset() będzie brzmiała zawierała zapis, że domyślnym kodowaniem jest UTF-8 chyba że sostało to skonfigurowane inaczej w zależny od implementacji sposób.

Aby użyć czegoś innego niż UTF-8, użytkownicy będą mogli ustawić wartość właściwości file.encoding na:

  • COMPAT - która włączy zachowanie sprzed Javy 18
  • UTF-8 - która nic nie zmieni
  • inną wartosć - zachowanie nie jest w takim wypadku określone

Kodowanie System.out i System.err będzie w dalszym ciągu określane przy pomocy Console.charset().

Prosty serwer webowy

408: simple web server

Celem JEPa 408 jest:

Dostarczenie narzędzia wiersza poleceń pozwalającego na uruchimienie minimalnego serwera webowego, który jedynie udostępnia statyczne pliki. Nie jest dostępna funkcjonalność CGI ani funkcjonalność serwletów. Narzędzie to będzie użyteczne w przypadku prototypowania, kodowania ad-hoc, testowania, a szczególnie w kontekstach edukacyjnych.

Serwer w wierszu poleceń

Ten insteresujący JEP dostarcza Javowego odpowiednika istniejącego w Pythonie

1
python3 -m http.server

Serwer Javowy będzie uruchamiany dość podobnie:

1
$ java -m jdk.httpserver

co uruchomi serwer na domyśnym porcie 8000.

Można również podać inny port:

1
$ java -m jdk.httpserver -p 9000

albo zbindować się do wszystkich interfejsów (opcja -b - bind):

1
$ java -m jdk.httpserver -b 0.0.0.0

Domyślnie serwer będzie dostarczał pliki z bieżącego katalogu roboczego. Katalog ten można nadpisać używając opcji -p:

1
$ java -m jdk.httpserver -p data

Opcje wiersza poleceń

Opcja -h wyświetla prostą wiadomość (pomoc), która zawiera listę wsztystkich dostęonych opcji:

1
2
3
4
5
6
7
8
Options:
-b, --bind-address    - Address to bind to. Default: 127.0.0.1 or ::1 (loopback).
                        For all interfaces use "-b 0.0.0.0" or "-b ::".
-d, --directory       - Directory to serve. Default: current directory.
-o, --output          - Output format. none|info|verbose. Default: info.
-p, --port            - Port to listen on. Default: 8000.
-h, -?, --help        - Print this help message.
To stop the server, press Ctrl + C.

Notatki

  • wspierany jest jedynie protokół HTTP (HTTPS nie jest wspierany)
  • typy mime są ustalane autoatycznie (pliki .html są serwowane jako ext/html a pliki .java jako text/plain) - może to zostać zmienione przy użyciu API java.net.URLConnection::getFileNameMap
  • każde żądanie jest logowane na konsolę (domyślnie serwer zachowuje się tak, jakby w wierszu poleceń przekazana była opcja -o info; aby wyłączyć logowanie można użyć opcji -o none; aby włączyć pełne logowanie - opcji -o verbose)
  • serwer można “zanurzyć” w innych programach przy użyciu nowego API: SimpleFileServer, HttpHandler i Request (prawdopodobnie w pakiecie com.sun.net.httpserver)

SimpleHttpServer i nowe API

Do programowego utworznenia serwera służy nowa klasa SimpleHttpServer, która pozwala również zdefiniować handler oraz filter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.sun.net.httpserver;

public final class SimpleFileServer {
    public static HttpServer createFileServer(InetSocketAddress addr,
                                              Path rootDirectory,
                                              OutputLevel outputLevel) {...}
    public static HttpHandler createFileHandler(Path rootDirectory) {...}
    public static Filter createOutputFilter(OutputStream out,
                                            OutputLevel outputLevel) {...}
    ...
}

Utworzenie serwera w kodzie

W taki sposób można stworzyć i uruchomić prosty serwer plików:

1
2
3
4
5
var server = SimpleFileServer.createFileServer(
  new InetSocketAddress(8080),
  Path.of("/my/data/dir"), 
  OutputLevel.VERBOSE);
server.start()

Ten serwer:

  • dostarcza pliki z katalogu /my/data/dir
  • loguje żądania na poziomie verbose
  • nasłuchuje na połączenia na porcie 8080

Są to więc parametry, które odpowiadają parametrom wiersza poleceń, odpowiednio -d, -o, -b.

Inną możliwością jest użycie statycznej metody create która została dodana do dwóch istniejących wcześniej klas: HttpServer and HttpsServer.

1
2
3
4
5
public static HttpServer create(InetSocketAddress addr,
                                int backlog,
                                String root,
                                HttpHandler handler,
                                Filter... filters) throws IOException {...}

Można jej użyć do utworzenia serwera i dostosowania go do swoich potrzeb.

Dostosowanie SimpleHttpServer

Co można dostosować? Na przykład można użyć różnych handlerów (funkcji obsługi) dla różnych kontekstów, przy czym do utworzenia handlera można użyć funkcji z nowego API SimpleFileServer:

Jak użyć dwóch handlerów do dwóch konstekstów?

Proszę spojrzeć:

  • kontest “/store/” jest obsługiwany przez zarejestrowany handler SomePutHandler który obsługuje żądania PUT
  • kontekst “/browse/” jest obsługiwany przez FileHandler utworzony przy użyciu nowego API SimpleFileServer.createFileHandler(), który będzie udostępniał pliki z katalogu “/my/data/dir”:
1
2
3
4
5
6
7
8
 var server = HtpServer.create(
  new InetSocketAddress(8080),
  10, 
  "/store/", new SomePutHandler());
var handler = SimpleFileServer.createFileHandler(
  Path.of("/my/data/dir"));
server.createContext("/browse/", handler);
server.start();

Jak utworzyć filtr strumienia wyjściowego?

Oto jak utworzyć serwer posiadający filtr strumienia wyjściowego.

  • najpierw tworzony jest filtr, który obejmuje standardowe wyjście procesu serwera (System.out)
  • taki filtr przekazany funkcji create spowoduje - jak sądzę - że SomePutHandler użyje standardowego wyjścia procesu obsługującego serwer jako strumienia, do którego będzie pisał odpowiedź
  • wobec czego każde żądanie PUT spowoduje wypisanie danych zapisanych do strumienia odpowiedzi przez SomePutHanlder na standardowe wyjście programu
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var filter = SimpleFileServer.createOutputFilter(
   System.out,
   OutputLevel.INFO);
var server = HttpServer.create(
    new InetSocketAddress(8080),
    10, 
    "/store/", 
    new SomePutHandler(), 
    filter);
server.start();

Rozszerzenie obsługi żądań

Nowa klasa HttpHandlers posiada dwie metody statyczne służące do utworzenia funkcji obsługi żądań:

1
2
3
4
5
6
7
8
9
package com.sun.net.httpserver;

public final class HttpHandlers {
    public static HttpHandler handleOrElse(Predicate<Request> handlerTest,
                                           HttpHandler handler,
                                           HttpHandler fallbackHandler) {...}
    public static HttpHandler of(int statusCode, Headers headers, String body) {...}
    {...}
}

Klasa abstrakcyjna Filter pozwala utworzyć filtr zmieniający lub przystosowujący istniejące żądanie:

1
2
3
4
5
public abstract class Filter {
    public static Filter adaptRequest(String description,
                                      UnaryOperator<Request> requestOperator) {...}
    {...}
}

Może być ona użyty w następujący sposób:

  • HttpHandlers.handleOrElse pozwala oddelegować obsługę żądania do inego handlera na podstawie stanu żądania
  • HttpHandlers.of do utworzenia handlera który zawsze odpowieada w taki sam sposób
  • Request.with() do zmiany bądź dodania nagłówka do każdego z nadchodzących żądań

Request jako niemodyfikowalny widok obiektu HttpExchange

Interesujące jest to, że filtry używają klasy HttpExchange, która reprezentuje pełny, mutowalny stan żądania, jednak nowa metoda klasy Filter: adaptRequest używa ograniczonego widoku stanu. Ten widok jest reprezentowany przez interfejs Request:

1
2
3
4
5
6
7
8
9


public interface Request {
    URI getRequestURI();
    String getRequestMethod();
    Headers getRequestHeaders();
    default Request with(String headerName, List<String> headerValues)
    {...}
}

Jest to jeszcze jeden argument w dyskusji o wyższości bytów niemutowalnych nad mutowalnymi - nowe API stara się używać minimalnych interfejsów oferujących jedynie operacje niezmieniające stanu. Warto zauważyć, że wiele ostatich (i tych trochę dawniejszych) zmian w bibliotece standardowej zostało zaprojektowanych tak, aby oferować interfejsy bądź klasy operujące na niemutowalnych obiektach (na przykład java.time) oraz rozszerzać je przez dodawanie metod zwracających obiekty niezmienialne (np. nowe API w kolekcjach Javy).

Przykład konfiguracji serwera

Oto kod, który tworzy serwer dostosowany do pewnych określonych potrzeb. To jego możliwości:

  • przekazuje obsługę żądań do różnych obiektów: SomePutHandler lub SomeHandler w zależności od typu metody HTTP użytej w żądaniu; jeśli metodą jest PUT, wywołany zostanie SomePutHandler
  • dodaje nagłówek Foo z waartością Bar
  • nasłuchuje na porcie 8080
  • odrzuci nowe połączenia przychodzące, jeśli w danej chwili jest już 10 połączeń aktywnych
  • będzie działał w kontekście “/”
1
2
3
4
5
6
jshell> var h = HttpHandlers.handleOrElse(r -> r.getRequestMethod().equals("PUT"),
   ...> new SomePutHandler(), new SomeHandler());
jshell> var f = Filter.adaptRequest("Add Foo header", r -> r.with("Foo", List.of("Bar")));
jshell> var s = HttpServer.create(new InetSocketAddress(8080),
   ...> 10, "/", h, f);
jshell> s.start();

Fragmenty kodu w dokumentacji

413: code snippets in java api documentation

Przejdźmy do kolejnego JEP-a.

Pisanie porządnej, użytecznej i mądrej dokumentacji do kodu wymaga sporo umiejętności i jest znacznie łatwiejsze z dobrymi narzędziami. Standardowym narzędziem w Javie jest javadoc używający domyślnie docletu Standard Doclet. Writing solid, usable and informative code documentation requires both skills and tools. The standard tool is javadoc which by default uses Standard Doclet.

Czym jets javadoc i doclet

Doclet jest programem napisanym w Javie który używa Javadoc API do tego, aby przeanalizować kod w Javie i wygenerować dokumenty HTML zawierające opisy kodu znajdujące się w komentarzach.

Doclet API (zwane też javadoc API) pozwala na utworzenie klasy pochodnej po Doclet i użycie jej nazwy jako paramertru dla opcji -doclet podczas wywołania narzędzia javadoc:

1
    javadoc -doclet <your Doclet subclass> -sourcepath <source-location> java.util

To tylko szybkie przypomnienie, czym jest doclet. Oczywiście, programiści zwykle nie są zainteresowani pisaniem docletow. Chcą napisać kod, opatrzyć go komentarzami i wygenerować dokumentację w HTML-u (co zwyle odbywa się jako jeden z kroków procesu budowania kodu).

Używają po prostu polecenia javadoc ze standardowym, domyślnym docletem i piszą komentarze używając tagów (w postaci @tag) oznaczajacych pewne semantycznie fragmenty.

Tag {@snippet …}

Java 18 będzie zawierać nowy tag javadocowy: - @snippet - który uprości dodawanie fragmentów kodu źródłowego do komentarzy dokumentujących użycie API.

W obecnym podejściu trzeba przykładowy kod umieścić wewnątrz tagów pre i oznaczyć jako @code: <pre> {@code ….} </pre>, jednak to podejście ma kilka istotnych wad:

  • narzędzia nie mogą w niezawodny sposób zastosować kolorowania składni, ponieważ nie ma sposobu na poinformowanie ich o tym, jakiego rodzaju kod znajduje się w bloku @code
  • w bloku nie można używać komentarzy /* … */
  • fragmenty kodu nie mogą używać znaczników HTML
  • fragmenty kodu nie mogą używać komentarzy zawierających tagi linkujące do innych miejsc w API

Nowy tag @snippet:

  • może definiować snippet wewnętrzny (zawarty bezpośrednio w komentarzu w kodzie źródłowym) bądź snippet zewnętrzny (zapisany w innym pliku)
  • może definiować pary klucz-wartość, zwane atrybutami:
    • id definiuje identyfikator snippeta i umożliwia utworzenie odnośnika
    • lang określa język programowania użyty w snippecie
    • file określa położenie zewnętrznego pliku ze snippetem
    • region deklaruje nazwę regionu, który zostanie zdefiniowany przy użyciu tagów @start i @end w komentarzach wewnątrz snippeta
  • może zawierać kod w języku Java, zawartość plików .properties, kod w innych językach programowania bądź zwykły tekst
  • wewnętrzny snippet może zawierać tagi znacznikowe wskazujące określone regiony bądź wiersze i zawierające instrukcje, jak te regiony/wiersze zaprezentować

Dwa ograniczenia, które dotyczą wszystkich tagów javadocowych, dotyczą również nowego taga @snippet:

  • wewnętrzny tag nie może używać komentarzy /* … */ ponieważ komentarz zamykający zamknie zawierający go początek komentarza dokumentującego
  • tag wewnętrzny musi zawierać zbalansowane pary nawiasów klamrowych (po to, aby poprawnie określić początek i koniec bloku @snippet)

Przykład wewnętrznego @snippet-a

1
2
3
4
5
6
7
8
/**
 * The following code shows how to use {@code Optional.isPresent}:
 * {@snippet :
 * if (v.isPresent()) {
 *     System.out.println("v: " + v.get());
 * }
 * }
 */

Przykład zewnętrznego @snippet-a z definicją regionu

Snippet zewnętrzny (tj. tag @snippet wskazujący na zewnętrzne źródło kodu, który powinien znaleźć się w komentarzu) zawiera:

  • atrybut file wskazujący nazwę pliku, w którym znajduje się przykład
  • atrybut region deklarujący region o nazwie example który wraz z tagami @start i @end oznacza granice fragmentu kodu, który ma zostać włączony jako zawartość snippetu w javadocu
1
2
3
4
/**
 * The following code shows how to use {@code Optional.isPresent}:
 * {@snippet file="ShowOptional.java" region="example"}
 */

gdzie ShowOptional.java jest plikiem zawierającym następującą treść:

1
2
3
4
5
6
7
8
9
public class ShowOptional {
    void show(Optional<String> v) {
        // @start region="example"
        if (v.isPresent()) {
            System.out.println("v: " + v.get());
        }
        // @end
    }
}

Oznaczenie/kolorowanie składni

Do oznaczania składni można użyć tagu @highlight, po którym mogą się pojawić argumenty:

  • // @highlight substring="println" - oznacza wskazany podciąg
  • // @highlight region regex = "\barg\b" po którym następuje @end - oznacza region, w którym oznaczanie składni ma być zastosowane dla napisów pasujących do wyrażenia regularnego
  • type to parmetr, który może przyjmować wartości bold, italic lub highlighted; zostanie on przekształcony w klasę css o tej samej nazwie, a jej definicję moża umieścić w odpowiednim arkuszu stylów (systemowym bądź zdefiniowanym przez użytkownika)
  • @replace regex='".*"' replacement="..." - zamienia pasujące do wyrażenia regularnego napisy na wskazany zamiennik (tu: trzykropek)

Tworzenie odnośników

Snippet może również zawierać odnośniki do zewnętrznej dokumentacji. Na przykład poniższy kod wygeneruje javadoc z odnośnikiem do dokumentacji System.out:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/**
 * A simple program.
 * {@snippet :
 * class HelloWorld {
 *     public static void main(String... args) {
 *         System.out.println("Hello World!");  // @link substring="System.out" target="System#out"
 *     }
 * }
 * }
 */

Używanie snippetów z właściwościami

Snippety mogą również zawierać fragmenty plików .properties z właściwościami:

1
2
3
4
5
6
7
8
9
/**
 * Here are some example properties:
 * {@snippet lang=properties :
 *   local.timezone=PST
 *   # @highlight regex="[0-9]+" :
 *   local.zip=94123
 *   local.area-code=415
 *   }
 */

Sprawdzanie poprawności

Autorzy propozycji JEP obiecują rozszerzenie API drzewa kompilacji , które uwzględni tag @snippet. Dzięki temo możliwe będzie stworzenie osobnych narzędzi, które sprawdzą poprawność snippetów:

  • zewnętrzne snippety będą mogły być kompilowane (przy zachowaniu pewnego określonego kontekstu, tj. classpatha czy modulepatha)
  • wewnętrzne snippety będą mogły być opakowane kodem w taki sposób, aby tworzyły poprawną jednostkę kompilacji, czyli również będa mogły być skompilowane

Wydaje się więc, że począwszy od Javy 18 dokumentacja stanie się jeszcze bardziej czytelna, szczególnie w części pokazującej przykładowe użycie opisywanego API.

Ponowna implementacja refleksji

416: reimplement core reflection with method handles

Ten JEP opisuje wewnętrzne zmiany w java.lang.reflect oraz java.lang.invoke, które nie mają wpływu na API.

Ich celem jest użycie uchwytów metod (Method Handles) jako mechnizmu do zaimplementowania refleksji. Obecnie istnieją jeszcze dwa sposoby implementacji reflekcji:

  • wywołanie kodu natywnego w HotSpot VM (tylko na początku wykonania programu po to, aby skrócić czas uruchomienia) - ten sposób implementacji refleksji będzie wciąż obecny na wczesnych etapach działaniamaszyny wirtualnej
  • drugi sposób to dynamiczne generowanie bajtkodu dla metod takich jak Method::invoke, Constructor::newInstance, Field::get oraz Field::set - zostanie on zastapiony użyciem java.lang.invoke API wprowadzonym w Javia 7

Zgodność

Dotychczasowa implementacja będzie wciąż dostępna pod flagą: -Djdk.reflect.useDirectMethodHandle=false

API wektorowe

417: vector api (third incubator)

Ten JEP (a właściwie jego implementacja) będzie po raz trzeci na etapie “incubator”, co oznacza, że wciąż nie jest to API, które można uznać za dojrzałe ani stabilne.

Pod wpływem otrzymanych od użytkowników informacji zwrotnych zostały zaimplemetowane kolejne poprawki i rozszerzenia.

Głównym celem tego JEP-a jest dostarczenie nowego API wektorowego, które jest niezależne od platformy. W czasie działania kod implementujący to API powinien w rozsądny sposón skompilować się do optymalnego kodu maszynowego, to jest takiego, który używa właściwych dla architektury procesora instrukcji wektorowych.

“API wektorowe” posiada dwie implenentacje:

  • czystą implementację w Javie (“funkjonalną ale nie optymalną”)
  • implementację operacji wektorowych w kompilatorze HotSpot C2, w których przetwarzanie odbywa się na odpowiednich rejestrach wektorowych przy użyciu stosownych wektorowych insktrukcji CPU

Do reprezentacji wektora zostanie użyty typ Vector, gdzie E (np. Integer) będzie zapudełkowanym (zboksowanym?) typem podstawowym (np. int. Vector i jego podklasy są klasami-wartościami (value classes), przy czym po wdrożeniu projektu Valhalla typy podstawowe będą mogły być rozszerzone na klasy podstawowe.

Dokumentacja JEP-a wspomina, że obecnie będzie wspierany jedynie Linux i Windows; MacOS będzie musiał jeszcze poczekać.

SPI do uzyskiwania adresów internetowych

418: internet-address resolution spi

Ten JEP przygotowuje interfejs dla dostarczycieli usług (service provider interface), którzy chcą implementować uzyskiwanie nazw hostów oraz adresów. Dzięki temu java.net.InetAdress będzie mógł używać innych sposobów uzyskiwania adresów niż domyślny, który jest bardzo prosty.

Domyślnie bowiem uzyskiwanie adresów odbywa się na podsawie pliku hosts oraz DNS. Jednak:

  • obecnie uzyskiwanie adresu jest wywołaniem blokującym bieżący wątek (z podowu blokującego wywołania systemowego); jest to problematyczne dla projektu Loom, który chce używać wątków wirtualnych i wobec tego chce mieć możliwość uzyskiwania adresów w sposób nieblokujący (tak, aby bieżący wątek mógł - podczas oczekiwania na wywołanie systemowe - zacząć obsługiwać inne wątki wirtualne)
  • istnieją inne protokoły, które mmogą być obsługiwane (takie jak TLS czy HTTPS)
  • frameworki będą miały bardzo precyzyjną kontrolę nad uzyskiwaniem adresów
  • uprości się prototypowanie i testowanie, ponieważ będzie można, na przykład, napisać zaślepkę (“zamokować”) udającą pewien komponent sieciowy używający API InetAddress

API do wywołań zewnętrznych i dostępu do pamięci

419: foreign function & memory api (second incubator)

Ten JEP jest w drugiej fazie inkubacji. Skupia się na dostarczeniu nowego API, przy pomocy którego programy w Javie będą mogły używać kodu i danych spoza runtime-a javowego.

API pozwoli kodowi klienckiemu w bibliotekach i aplikacjach wykonywać następujące operacje (w nawiasach podano nowe klasy w Javie, które definiują FFM - Foreign Funciton & Memory - API):

  • przydzielić zewnętrzną pamięć (MemorySegment, MemoryAddress, and SegmentAllocator)
  • uzyskać dostęp i zmieniać tę pamięć (MemoryLayout, VarHandle)
  • zarządzać cyklem życia zewnętrznych zasobów (ResourceScope)
  • wywoływac zewnętrzne funkcje (SymbolLookup, CLinker, and NativeSymbol)

Dopasowanie wzorca w switch

420: pattern matching for switch (second preview)

To zmiana będąca drugim preview i w porównaniu z pierwszym preview z JEP-a 406 zawiera pewne małe rozszerzenia:

  • stała w labelce case musi się znaleźć przed wzorcem ze strażnikiem (warunkiem) tego samego typu: przykład
  • bardziej precyzyjne sprawdzanie “wyczerpywalności” wzorców w przypadku hierarchii klas “zaplombowanych” (sealed): przykład

Podsumowanie

Większość planowanych w wydaniu 18 Javy zmian to rozszserzenia istniejącego kodu. Tylko trzy pierwsze JEP-y w powyższej liście wprowadzają coś nowego z perspektywy przeciętnego programisty aplikacji. Są to:

  • UTF-8 jako domyślne kodowanie

  • snippety kodu jako roszerzenie dokumentacji

  • prosty http server do celów testowania

    To na tych JEP-ach skupiłam się w tym wpisie, przede wszystkim dlatego, że wydaje mi się, że je rozumiem 😄

    Inne, takie jak API wektorowe czu FFI - wydają mi się bardzo złożone, a ja nie czuję się ekspertem w żadnej z tych dziedzin. W zasadzie rozumiem tylko podstwowe przypadki ich użycia, przy czym rozumiem jedynie teoretycznie…

    Przykładowo: nigdy nie użyłam w swoim kodzie JNI a więc pewnie nie docenię abstrakcji i użyteczności FFM API. Podobnie też nigdy nie tworzyłam potoków przetwarzających wektorowo dane obrazu (choć wyobrażam sobie, że przydałoby się tu API wektorowe, podobnie jak do przetwarzania sygnałów w sztucznych sieciach neuronowych).

    Patrzę na ewolucję JEP-ów i widzę, jak platforma Javy (choć raczej platforma JVM) zmienia się i rozrasta na wiele nowych obszarów. Pozwala mi to czuć miłe ciepełko i otuchę, że przecież Java is not dead a JVM to platforma, przy której chcę zostać jako programista i w którą warto - jak sądzę - inwestować swój czas i energię.

Podziękowania

Zdjęcie: NASA źródło: Unsplash