Dawno nie było JEP-ów, prawda? Też się stęskniłem, także mam dla Was dzisiaj aż trzy. Oprócz tego nowy IntelliJ Idea z wbudowanym AI Assistant.
1. Nowe JEP-y: Computed Constants, nowe podejście do Ahead-of-Time & stabilizacja i FFM API
JEP draft: Foreign Function & Memory API
Zaczniemy od czegoś dość oczywistego, ale w dalszym ciągu interesującego.
JDK 21 przynosi stabilizacje wielu długo oczekiwanych API, ale z jednym dość istotnym wyjątkiem – Foreign Function & Memory API. Ta kluczowa funkcjonalnosć Panamy ukaże się wyłącznie jako Preview, już trzecie. Wszystko wskazuje jednak, że również ostatniego – w ostatnich dniach opublikowany i zaktualizowany został JEP Draft zawierający już specyfikacje finalnej wersji tego API. Wszystko wskazuje więc na to, że któraś z najbliższych wersji Javy (może już JDK 22) pozwoli nam na wygodne Sam JEP zaś skupia się na finalizacji interfejsu API FFM poprzez wprowadzenie serii ulepszeń, takich jak wprowadzenie atrybutu manifestu Enable-Native-Access
, umożliwiającego plikom JAR dostęp do wybranych metod bez wymagania konkretnych flag. Dodatkowe ulepszenia obejmują tworzenie deskryptorów funkcji C, bardziej zaawansowane wsparcie dla natywnych tablic o zmiennym rozmiarze oraz wsparcie dla wielu zestawów znaków w natywnych stringach.
JEP draft: Computed Constants
Programiści Java stają przed dylematem między niezmiennością a elastycznością inicjalizacji, szczególnie w zarządzaniu polami final
. Java wymaga bowiem, aby ta była inicjalizowana w konstruktorze:
class DeepThought {
final int theAnswer;
public DeepThought() {
value = ultimateQuestion();
}
private int ultimateQuestion() {
// Expensive computation here
return 42;
}
Oczywiście istnieją strategie takie jak class-holder idiom:
class DeepThought {
private static class AnswerHolder {
static final int theAnswer = ultimateQuestion();
private static int ultimateQuestion() {
// Expensive computation here
return 42;
}
}
public DeepThought() {
}
public int getTheAnswer() {
return AnswerHolder.theAnswer;
}
}
czy (w nieco bardziej skomplikowanych przypadkach) znany choćby z Effective Java, paskudny double-checked locking:
class DeepThought {
private static class AnswerHolder {
static final int theAnswer = ultimateQuestion();
private static int ultimateQuestion() {
// Expensive computation here, takes 7.5 million years
return 42;
}
}
public int getTheAnswer() {
return AnswerHolder.theAnswer;
}
}
class LazySingleton {
private static volatile LazySingleton instance;
private final DeepThought deepThought;
private LazySingleton() {
deepThought = new DeepThought();
}
public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
public int getTheAnswer() {
return deepThought.getTheAnswer();
}
}
choć użyteczne, mają wady, takie jak dodatkowa złożoność połączona z utratą możliwości łatwej optymalizacji w czasie wykonywania. W przeciwieństwie do języków takich jak C# (z jego typem Lazy<T>
):
class DeepThought {
private static readonly Lazy<int> theAnswer = new Lazy<int>(() => UltimateQuestion());
private static int UltimateQuestion() {
// Expensive computation here
return 42;
}
static int getTheAnswer() {
return theAnswer.get();
}
}
czy Kotlin (który lazy
dla „leniwej” inicjalizacji):
class DeepThought {
val theAnswer: Int by lazy { ultimateQuestion() }
private fun ultimateQuestion(): Int {
// Expensive computation here
return 42
}
}
ścisłe wymogi Javy dotyczące inicjalizacji pól oznaczonych jako final
stwarzają wyzwania w odroczeniu inicjalizacji stałych, zmuszając programistów do obchodzenia ograniczeń języka .
JEP draft: Computed Constants wprowadza koncept Computed Constants, mających na celu zniwelowanie tej luki poprzez dostarczenie funkcji podobnej do konstrukcji znalezionych w językach takich jak C# i Kotlin. Stałe Obliczeniowe (nawet zgrabne tłumaczenie) są niemutowalne i są gwarantują inicjalizowane co najwyżej raz, oferując korzyści pól oznaczonych jako final
, ale z większą elastycznością w czasie inicjalizacji.
class DeepThought {
private static final ComputedConstant<Int> theAnswer =
ComputedConstant.of(() -> ultimateQuestion());
static int ultimateQuestion() {
// Expensive computation here
// (evaluation made before the first access)
return 42
}
static int getTheAnswer() {
return theAnswer.get();
}
}
Jak widzicie, rozwiązanie jest całkiem eleganckie i ładnie wpasowuje się w ostatnie konwencje języka, takie jak Scoped Values. Wspominany już oryginalny JEP zawiera też przykład dla kolekcji.
JEP draft: Ahead of Time Compilation for the Java Virtual Machine
A na koniec chyba moje ulubione mięsiwo, wracamy bowiem do uwielbianego przeze mnie tematu szybszego startu JVM.
Aplikacje i biblioteki Java są obecnie wykonywane w modelu trzystopniowym, najpierw w Interpreterze, następnie jako skompilowany kod C1, a potem jako skompilowany kod C2. Proces ten jest bardzo dynamiczny, obejmujący wiele iteracji cykli optymalizacji i deoptymalizacji w trakcie życia kodu. Dodatkowo oznacza to, że rozgrzewanie kodu (gdzie w runtime odbywa się kompilacji wysoce zoptymalizowanej wersji) może zająć dużo czasu.
Dobrym przykładem jest kompilator Graal, który (jak pewnie niewiele osób już pamięta) był dostępnym zamiennikiem dla C2 przed Java 17, gdzie w spektakularny sposób się go pozbyto. Powody były całkiem logiczne: etap uruchamiania kompilatora negatywnie wpłynął na całą aplikację, ponieważ sam Graal musiał zostać skompilowany, zanim mógł działać z rozsądną prędkością, co zniwelowało możliwe zyski z wydajności. Dodatkowo proces deoptymalizacji, który występuje, gdy skompilowany kod C2 napotyka błędną hipotezę, może być kosztowny, ponieważ wariant profilujący C1 musi zostać ponownie skompilowany, tylko po to, by zostać ponownie odrzucony, gdy C2 skompiluje tę samą metodę. Istniejące alternatywy, jak wspomniany GraalVM i Native Image, operują zaś na tak zwanym „założeniu zamkniętego świata”, które nie pozwala na dynamiczne doczytywanie kolejnych klas w runtime. Wymuszają więc spore zmiany w stosunku do typowym wykonywaniem aplikacji Java przez JVM.
Dobre rozwiązanie to posiadanie trwałego wariantu metody skompilowanej Ahead-of-Time C1, dzięki czemu można pominąć Interpreter i C1, tak że przy uruchamianiu wykonanie zaczyna się od prekompilowanego kodu C1, a następnie natychmiast przechodzi do kompilacji C2. Proponowane w JEP draft: Ahead of Time Compilation for the Java Virtual Machine rozwiązanie polega więc na wzbogacenie JVM o zdolność do wczytywania aplikacji i bibliotek Java skompilowanych do kodu natywnego przed uruchomieniem. Obejmuje to całkowite pominięcie Interpretera, umożliwiając kompilację kodu Java, standardowych bibliotek i dowolnych komponentów JVM Ahead-of-Time do natywnego kodu w trybach profilowania lub optymalizacji. Zmiana ta umożliwia natychmiastowe wykonywanie skompilowanego kodu, przechodzenie od razu do kompilacji C2 oraz zachowanie skompilowanych metod dla bardziej wydajnych procesów deoptymalizacji.
Brzmi trochę jak mix podejścia CRaC z projektem Leyden, prawda? Ten ostatni jest nawet wspominany w oryginalnym JEP-ie, a korzystając z okazji pozwolę sobie na mały spoiler: Do tematu Leyden wrócimy już niedługo, bo zaledwie wczoraj ukazały się nowe aktualizacje w temacie i idę się musiał przez nie przebić i przetrawić, bo to jednak nieco bardziej skomplikowany temat i nie chce Wam dostarczyć czegoś na szybko, niedopieczonego.
Zainstaluj teraz i czytaj tylko dobre teksty!
2. Premiera nowego IntelliJ Idea z AI Assistant
Pojawił się nowy IntelliJ i oczywiście, jak każde narzędzie w 2023 musiał doczekać się wsparcia AI.
Generatywna sztuczna inteligencja i tak zwane LLM (Large-Language Models) zostały zintegrowane z IDE JetBrains, a celem nowego featura ma być (oczywiście) zwiększenie efektywności pracy programistów. Obejmuje to czat umożliwiający tworzenie zapytań o kod, automatyczne generowanie dokumentacji, sugestie nazw i generowanie commit message. Funkcje AI są dostarczane przez usługę JetBrains AI, które na ten moment opierają się na OpenAI, ale w twórcy obiecali, że należy się spodziewać wsparcia innych dostawców, a także lokalnych modeli. Niestety, całość dostępna jest tylko dla wybranych użytkowników. Wszystko powyższe pisze w oparciu o release notes, ponieważ czekam ciągle na dostęp 🤷♂️
To nie koniec ulepszeń w IntelliJ IDEA 2023.2. Java doczekała się rozszerzonej inspekcji i podświetlania kodu oraz lepsze wsparcie dla wprowadzonego w JDK 18 tagu @snippet
w komentarzach Javadoc. Stringi SQL w Java i Kotlin otrzymały lepszą analizę w celu wykrycia niebezpiecznych zapytań i potencjalnych podatności na ataki typu SQL injection.
Nowa wersja wzmacnia również wsparcie dla Scala 3, wprowadzając usprawnienia takie jak lepsze wykorzystanie enumów, dekompilator TASTy, wsparcie w edytorze dla IArray
oraz lepsze wsparcie dla składni o mniejszej ilości nawiasów. IDE posiada ulepszoną obsługę katalogów oraz usprawnia renderowanie ScalaDoc.
Oprócz Asystenta AI i innych wspomnianych funkcji, nadchodząca wersja IntelliJ IDEA 2023.2 będzie oznaczać wycofanie kilku wtyczek, takich jak Struts2, Play i Cloud Foundry – nie będą one już aktualizowane. Mnie jakoś łezka się w oku zakręciła jeśli chodzi o Play Framework, który kiedyś uważany był za nową jakość w świecie JVM, a teraz po oddaniu go społeczności przez Lightbend jest przestarzały i brakuje mu pełnej zgodności z Java 17, aczkolwiek trzeba docenić starania Matthiasa Kurza, który dba o jego utrzymanie przy wsparciu Open Collective. Co ciekawe, wersja ta zakończy również wsparcie dla Windows 7, wymagając aktualizacji do Windows 10 lub nowszego systemu operacyjnego.
Zainstaluj teraz i czytaj tylko dobre teksty!
3. A może widzimy się na KotlinConf ’24?
A na koniec, jak już piszemy o produktach JetBrains, to już totalna drobnica.
Zapowiedziano bowiem piątą edycję KotlinConf, która odbędzie się od 22 do 24 maja 2024 roku w Bella Center w Kopenhadze, w Danii.
KotlinConf’24 rozpocznie się dniem warsztatów, a następnie przez dwie intensywne dni będzie oferować sesje i możliwości nawiązywania kontaktów i wiele innych atrakcyjnych aktywności. Swoimi wrażeniami z zeszłorocznego Keynote dzieliłem się na wpisie TLDW: Opinionated Wrap-up of KotlinConf 2023 Keynote, więc możecie zobaczyć na ile tego typu impreza jest dla Was interesująca. Pisze o tym, bo radziłbym się pospieszyć z zakupem – zarówno bilety w promocji super wczesnej, jak i wczesnej już zostały wyprzedane, a nie minął jeszcze tydzień od zapowiedzi.
A, i wzmianka jest totalnie bezinteresowna, nie mam z niej żadnej affiliacji – ale jeśli czyta to ktoś, kto może mnie pchnąć w kolejce AI Assistanta, to akurat byłbym wdzięczny 😄
A jak już w temacie Kotlina, to mam dla Was ostatnią małą informacje: Aby rozwiązać zamieszanie i niespójność w nazewnictwie, nazwa produktu „Kotlin Multiplatform Mobile” (KMM) jest wycofywana. Od tej pory „Kotlin Multiplatform” (KMP) to oficjalna nazwa kotlinowych rozwiązań służących do współdzielenia kodu na różnych platformach, niezależnie od konkretnej kombinacji platform docelowych. Szczegóły i kontekst tej zmiany znajdziecie w artykule Update on the Name of Kotlin Multiplatform.
PS: Odpowiedź na pytanie zadane w tekście: Expensive computation trwało 7.5 miliona lat 🐁.