Wprowadzenie LINQ do C# też spowodowało wielkie halo. Zarówno streamy z Javy 8+ jak i LINQ w C# zmieniły sporo w wyglądzie kodu. Natomiast okazjonalna zmiana z List<Integer>
na List<int>
wygląda przy tym na zmianę kosmetyczną.
No, to potwierdza, że jak wejdą Valhalle i inne cuda, to będzie o tym bardzo, bardzo głośno w świecie Javy. Już nie mogę się doczekać tych setek artykułów tłumaczących czym są te "inline class" (czy jak to się teraz zwie), ile będzie pokazywania przekazywania parametrów do funkcji i tak dalej.
O Javie to generalnie jest mnóstwo artykułów i tak już zostanie. Dla przykładu, artykułów o tym, że "Java is dying" było co najmniej miliard. Nawet gospodynie domowe musiały o tym czytać, bo tak się temat nosił.
Generalnie zgadzam się, że te typy dla funkcji w bibliotece standardowej Javy pozostawiają sporo do życzenia, ale nadal nie jestem przekonany co do skali wpływu tego na typowy biznesowy kod.
Spoko, możemy się odbijać argumentami na bycie przekonanym lub nie, to nie jest meritum.
To może napiszę o swoich doświadczeniach w Scali, języku dużo bardziej polegającym na lambdach niż Java. Otóż piszę kod biznesowy w stylu mieszanym FP + imperatywny OOP (czyli bez monad IO i szaleństw a'la Hascalator
/Haskellator
) i generalnie rzadko zdarza mi się pisać deklarację typu funkcji. W 95%+ lambdy wykorzystywane są tak, że je definiuję w miejscu, w którym są oczekiwane, bez podawania jawnych typów. Tutaj składnia się mocno nie różni, w Javie, C#, Scali, JSie, itd wygląda mniej więcej tak:
(param1, param2, param3) => { ciało lambdy }
Może jestem dziwny (obstawiam, że nie), ale rzędy wielkości częściej definiuję lambdę (do czego jest zwięzły cukier składniowy w Javie) niż deklaruję jej typ (do czego nie ma jeszcze cukru w Javie).
Zwykle jak szukam informacji o typach w Haskellu to znajduję informację, że typy są tam wymazywane, np:
Ale to nie chodzi o to, że typy są wymazywane, tylko o to, że nie da się pisać sensownie generycznego kodu. W dotnecie reifikowane generyki też są problematyczne, bo CLR nie wspiera HKT, więc jest to plus dla JVM. W Scali da się o wiele sensowniej pisać kod generyczny, mimo że działa na tej samej platformie. Erasure sam w sobie nie jest zły ani dobry.
Wspomniane HKT, a także inne zaawansowane zabawy z typami, to nie jest zabawa dla typowego klepacza kodu. Typowe zastosowanie Javy to pisanie względnie prostego kodu, który ma implementować nudne wymogi biznesowe, np. parsujemy X typów badziewnych raportów czy innych dokumentów, tworzymy Y obleśnych formularzy, integrujemy się z Z nieprzyjemnych w obsłudze API zewnętrznych. W takich przypadkach, przy założeniu że trzymamy się imperatywnego OOPa, to moim zdaniem aktualna Java jest adekwatna (w sensie jej generyki). W żadnym miejscu nie polecałem języka Java (nie mylić z platformą Java) do zaawansowanych zabaw z typami czy do programowania funkcyjnego w ogólności.
@jarekr000000 wspomniał w komentarzu o https://openjdk.java.net/projects/loom/ (projekt dotyczący między innymi modelu wielowątkowości). To jest coś co moim zdaniem będzie miało rzędy wielkości większy wpływ na postać kodu biznesowego niż https://openjdk.java.net/projects/valhalla/ (bo typowy klepacz kodu biznesowego i tak nawet nie widzi, że pętla jest O(n*n)
, a mogłaby być szybko przerobiona na O(n)
, więc co tu mówić o innych optymalizacjach). W ostatniej dekadzie jest bardzo dużo ruchu w temacie programowania nieblokującego czy też asynchronicznego (te pojęcia są mylone, w sumie to ciężko rozróżnić co kto ma na myśli czasem) i powstało dużo różnych rozwiązań, z których większość jest nieprzyjazna programiście, cierpi na problem kolorowania funkcji i zaśmieca kod biznesowy. Dla przykładu:
- callback hell (ale to już generalnie przeszłosć i raczej jest zawężone do starego kodu JSowego)
- operowanie na
Task<T>
(czy też nazwane Future, CompletableFuture, ReactiveBullcrap, cokolwiek) wymaga ciągłego używania kombinatorów a'la andThen
, thenMap
itp itd Problem jest odczuwalny nawet w Scali, mimo niemałych ilości cukru składniowego.
- async/await jest bardziej przyjazną składnią, ale dalej cierpi na problem kolorowania funkcji. Node.js ma podobno dużo zduplikowanych funkcji z których jeden zestaw jest synchroniczny, a drugi nie.
Natomiast Project Loom wprowadza lekkie wirtualne wątki (tak teraz je nazywają, virtual threads
, a wcześniej było fibers
), które można i nawet należy blokować, bo są bardzo lekkie, a przełączanie między nimi jest bardzo tanie (JVMka może spokojnie skalować się do milionów wirtualnych wątków jednocześnie). Bardzo możliwe, że (w Javie po wprowadzeniu project loom) concurrency
będzie zrobione nawet lepiej niż w golangu, a przecież głównym atutem Go jest właśnie programowanie współbieżne.
Podsumowując:
W moich rozważaniach skupiam się na wykorzystywaniu Javy do typowych zastosowań biznesowych, a nie jako języku do wszystkiego. W typowych zastosowaniach biznesowych język Java radzi sobie całkiem dobrze (chociaż jest dość brzydki), ale nawet nie aspiruje do bycia językiem spełniającym wszystkie oczekiwania.