Nie było nas tydzień temu (miałem wystąpienie na JUGu i czasowo już nie wyrobiłem), ale to nie znaczy, że też jakoś bardzo wiele się wydarzyło w ostatnie dwa tygodnie. Mam wrażenie, że po premierze JDK 20 weszliśmy w swoisty sezon ogórkowy, co jednak nie znaczy, że nie działo się nic ciekawego. Mimo wszystko, mam dzisiaj dla Was dwa JEPy oraz kilka nowych wydań, niektórych całkiem interesujących.
1. JEP 401: Implicitly Value Object Creation (Preview)
Projekt Valhalla kontratakuje! I materializuje kolejny zestaw zmian pod postacią JEP 401: Implicitly Value Object Creation (Preview) Jest to o tyle ciekawe, że cały proposal jeszcze niedawno nazywało się Null-Restricted Value Object Storage, a jeszcze wcześniej Primitive Classes.
Przechodząc jednak do meritum, ten JEP bierze sobie za cel poprawę wydajności przechowywania „klas wartości” (value classes). Value classes są klasami, które zostały zaprojektowane tak, aby mieć podobny profil wydajności do typów prymitywnych, ale z dodatkowymi korzyściami (możliwość tworzenia abstrakcji, enkapsulacja), które zapewniają klasy.
Klasy wartości zostały zaprojektowane tak, aby były szybsze i zużywały mniej pamięci niż zwykłe klasy. Są one jednak ograniczone w użyciu, ponieważ np. nie pozwalają na wartości null. Aby rozwiązać te ograniczenia, JEP wprowadza dwie nowe cechy: opcjonalne konstruktory i null-restricted types, o których wprowadzeniu dyskutowano swego czasu na listach mailingowych. Konstruktory opcjonalne pozwalają programistom tworzyć instancje value classes w sposób odmienny niż ma to miejsc w wypadku innych klas – możliwe jest np. pominięcie konstruktora. Typy z ograniczeniem nullowalności (za diabła nie wiem jak to ładnie przetłumaczyć) pozwalają programistom zadeklarować, że zmienna lub wartość zwracana przez metodę nie może mieć wartości null i automatycznie inicjują zmienne domyślną instancją klasy, gdy są one tworzone.
Żeby osiągnąć to ostatnie, posilę się fragmentem z oryginalnego JEP-a. Jeśli jesteście ciekawi jak będzie miał wyglądać dodatkowy syntax obsługi nulli:
interface Foo<T> {
T* get(); // Foo<char> returns char
T! getNonNull(); // Foo<char> returns char
T? getOrNull(); // Foo<char> returns Character?
T getOrAlternate(Supplier<T> alt); Foo<char> returns Character
}
Oczywiście, mówimy tutaj tylko i wyłącznie o Drafcie, więc wszystko się może zmienić. Żeby podkreślić, z jak wczesną wersją mamy do czynienia – JEP 401 linkuje do Nullness Marker JEP który… jeszcze nie istnieje. W chwili pisania tekstu, link zwraca odpowiedź 404.
Zainstaluj teraz i czytaj tylko dobre teksty!
2. JEP 443: Unnamed Patterns and Variables
Po dość skomplikowanym, bardzo wczesnym JEP-ie dotyczącym Valhalli przyglądniemy się nieco prostszemu JEP 443: Unnamed Patterns and Variables (Preview), który osiągnął niedawno status kandydata.
Zacznijmy od nienazwanych wzorców. Rozważmy następujące klasy rekordów:
record Point(int x, int y) {}
record ColoredPoint(Point point, String color) {}
Załóżmy, że chcemy wyodrębnić współrzędną x instancji ColoredPoint, ale nie zależy nam na jej kolorze. Możemy to zrobić za pomocą nienazwanego wzorca:
ColoredPoint coloredPoint = new ColoredPoint(new Point(3, 4), "red");
if (coloredPoint instanceof ColoredPoint(Point(int x, _), _)) {
System.out.println("x-coordinate is " + x);
}
W tym przykładzie „nienazwany wzorzec” jest reprezentowany przez znak podkreślenia _
w miejscu parametru color
we wzorcu. Dzięki temu możemy wyodrębnić współrzędną x
składnika Point
bez konieczności określania nazwy lub typu składnika color
.
Wiemy już czym są „Nienazwane wzorce”, przejdźmy teraz do czego odnoszą się „Nienazwane Zmienne”.
Rozważmy następujący kod, który wyciąga dane z kolejki i tworzy instancje Point2D
z listy współrzędnych w przestrzeni 3D.
Queue<Integer> queue = new LinkedList<>(List.of(1, 2, 3, 4, 5, 6));
List<Point2D> points = new ArrayList<>();
while (queue.size() >= 2) {
int x = queue.remove();
int y = queue.remove();
int z = queue.remove();
points.add(new Point2D(x, y));
}
Jak widzicie, w każdym przejściu pętli „dropujemy” wartość z. W takim przypadku możemy zadeklarować tą zmienne jako nienazwaną używając symbolu _
(tym razem Scala się kłania):
Queue<Integer> queue = new LinkedList<>(List.of(1, 2, 3, 4, 5, 6));
List<Point2D> points = new ArrayList<>();
while (queue.size() >= 2) {
var x = queue.remove();
int y = queue.remove();
var _ = queue.remove();
points.add(new Point2D(x, y));
}
Dzięki temu możemy skupić się na zmiennej x
i y
, które jako jedyne są ważne w tym kontekście. Symbol _
jest używana jako placeholder dla nieużywanej zmiennej i nie może być użyta nigdzie indziej w kodzie, ponieważ nie ma nazwy.
Przyznam szczerze, mnie powyższy przykład jakoś nie przekonał, jednak autorzy twierdzą, że nienazwane zmienne mogą być użyteczne w sytuacjach, gdy nazwy zmiennych są nieistotne lub gdy zmienne nie są używane i ma poprawić czytelność i utrzymanie kodu. Ponadto, nienazwane zmienne mogą pomóc w redukcji fałszywych alarmów generowanych przez narzędzia analizy statycznej, które zwracają uwagę na nieużywane zmienne. Ja osobiście największą szansę widzę w przypadku parametrów wyjątków, aczkolwiek poniższe też się jednak wydaje być pewnym code smellem:
try {
...
} catch (NumberFormatException _) {
System.out.println("Bad number");
}
3. Release Radar: Dropwizard 3.0 i 4.0
Pamiętam czasy, gdy używałem Dropwizarda, kiedy ten szumnie obiecujący framework znajdował się na samym szczycie popularności. Wydawało mi się wtedy, że to właśnie on zapowiada przyszłość tworzenia aplikacji internetowych i zostanie na dłużej w świadomości programistów. Niestety, życie i rynek potoczyły się inaczej. Mimo pierwotnego entuzjazmu, DropWizard zaczął tracić na znaczeniu, a inne narzędzia i technologie przyćmiły jego świetność. Dziś, kiedy obserwujemy równoczesną premierę wersji 3.0 i 4.0, to wydarzenie przechodzi bez echa w świecie technologii, co tylko świadczy o tym, jak bardzo zmieniła się percepcja tego niegdyś obiecującego projektu.
Co tak naprawdę przyciąga uwagę, to wspólna premiera wersji 3 i 4, co może być nieco mylące. Powód dwóch wersji jest prosty: Dropwizard 3.0.0 opiera się na Java EE i przestrzeni nazw javax.
, dzięki czemu migracja z Dropwizard 2.x do wersji 3.0.0 powinna być minimalna dla wielu projektów. Natomiast Dropwizard 4.0.0 opiera się na zależnościach Jakarta EE oraz przestrzeni nazw jakarta.
, co może wiązać się z większym nakładem pracy przy migracji z Dropwizard 2.x do wersji 4.0.0 ze względu na więcej zmian w pakietach i większą liczbę istotnych zmian w zależnościach.
Obie wersje łączy też trochę wspólnych zmian – podniesienie wymaganej wersji Javy do 11, wprowadzenie struktury pakietów opartej o JPMS (mam wrażenie, że to jedno z pierwszych narzędzi biorących ten standard na poważnie), aktualizację Jetty do wersji 10.0.x (która też wymaga minimum Javy 11), aktualizację Apache HttpClient do wersji 5.x oraz usunięcie wsparcia dla JUnit 4.x (przeniesione do dropwizard-testing-junit4
). Dodatkowo, Dropwizard 4.0 dostanie wsparcie dla Hibernate 6.0, wymagającego przejścia na jakarta.
Reakcja społeczności na Dropwizard 3.0 i 4.0 jest dość mieszana. Niektórzy programiści przyjmują nowe wydania z może nie ekscytacją, ale przynajmniej pewnym sentymentem (sam zaliczam się do tego grona). Zagorzali fani Dropwizarda mówią o jego „praktycznie pozbawionej błędów” naturze i wykorzystaniu sprawdzonych zależności. Jest jak stary, przytulny sweter, którego nie czujesz potrzeby wyrzucać wyrzucić. Doceniają również to, że Dropwizard nie jest maniakiem kontroli, w przeciwieństwie do niektórych innych frameworków (ekh ekh, Spring Boot), pozwalając programistom mieszać i dopasowywać komponenty do woli.
Są jednak i tacy, którzy uważają, że Dropwizard zaczyna pokazywać swój wiek, podobnie jak telefon z klapką, który znalazłeś w swojej szufladzie. Twierdzą, że na scenę wkroczyły bardziej eleganckie i błyszczące frameworki, takie jak Micronaut, Quarkus i Helidon, oferujące wymyślne funkcje, takie jak programowanie reaktywne, turbodoładowaną wydajność i bycie Cloud-Native.
Zainstaluj teraz i czytaj tylko dobre teksty!
4. Release Radar: Hibernate 6.20
A na koniec jeszcze nowy Hibernate ORM 6.2.0 Final.
Wydanie wprowadza strukturyzowane typy SQL, takie jak Struct, XML i JSON, co pozwala użytkownikom pracować z bardziej złożonymi strukturami danych. Powiązanym z tym dodatkiem jest wsparcie dla Rekordów, które umożliwia Hibernate deserializacje wspomnianych typów strukturalnych w obie strony.
Wydanie to również ujednolica adnotacje służące do generowania wartości. Co więcej, Hibernate oferuje teraz pełne wsparcie dla partycji bazy danych za pomocą adnotacji @PartitionKey
, co upraszcza proces mapowania kolumn partycji.
Ostatnią godną uwagi funkcją Hibernate ORM 6.2.0 jest implementacja SQL MERGE. Mechanizm ten działa w taki sposób nieco zbliżony do UPSERT. Jest jednak sporo potężniejszy, bowiem zamiast tylko aktualizować, umożliwia również usuwanie rekordów, a także umożliwia przekazanie dodatkowych warunków do zapytania, typu:
ON CONFLICT ON CONSTRAINT countries_pkey DO NOTHING;
ON CONFLICT (country) DO NOTHING;
Co prawda Hibernate używa go w sposób niewidoczny dla użytkownika końcowego, ale mnie ta nowość pozwoliła nieco poszerzyć swoją wiedzę o SQL, więc się dziele.
A, i był też Kotlin 1.8.20, ale jako że w przyszłym tygodniu będzie KotlinConf, to tym tematem zajmiemy się w następnej edycji, pewnie mocno kotlinowej.