Jako wielbiciel języków funkcyjnych wyczekiwałem Javy 8 z pewną dozą niecierpliwości. To jedno móc zawsze napisać coś w Haskellu, a zupełnie co innego mieć takie rzeczy jak lambdy we współczesnym COBOLu jakim jest Java – mimo wszystko póki co mogę liczyć raczej na znalezienie pracy przy programowaniu w Javie, a nie w Haskellu. Tak więc wyczekiwałem, wyczekiwałem, zapomniałem i ostatecznie premiera Javy 8 wzięła mnie z zaskoczenia. Ale gdy to nastąpiło, nie marnowałem czasu i natychmiast zacząłem się bawić nowymi zabawkami (zaniedbując trochę bieżące zadania „ważne”). W pewnym momencie doszło nawet do tego, że wyczekiwałem końca weekendu, żeby wrócić do pracy…. Ale to już przeszło. Po paru tygodniach eksperymentowania, naginania możliwości i przekraczania granic tego, co możliwe, dochodzę do wniosku, że jest nieźle, ale mogło być lepiej.
Mniej lub bardziej rozczarowujących rzeczy pewnie mógłbym wymienić więcej, ale tylko jedna z nich rzuciła mi się w oczy z wystarczającą siłą, żeby o niej napisać coś więcej, mianowicie to, że interfejsy funkcyjne nie mogą rzucać wyjątków. Może to efekt działania frakcji wyjątko-sceptyków, którzy uważają, że wyjątki (te „checked”) są pomiotem Szatana i najchętniej by je wymietli z języka ogniem i mieczem (też przechodziłem przez tę fazę, ale doświadczenia „po drugiej stronie” uświadomiły mi jednak, że błędem było myśleć, że wyjątki są błędem). Może to celowe działanie wynikające z innych pobudek, takich jak chęć uniknięcia zbędnej komplikacji interfejsów i/lub implementacji, czy też chęć uniknięcia kłopotliwych sytuacji, do których często prowadzą wyjątki jeśli się nie wie co się robi (ale i tak muszą obsługiwać choćby RuntimeException
więc co za różnica?). Może było to przedyskutowane na śmierć przez te wszystkie lata, kiedy dodanie lambd było odkładane do kolejnych wersji języka, i po prostu nie zainteresowałem się wystarczająco by mi się chciało poszukać argumentacji za i przeciw. W każdym razie doprowadza mnie do szewskiej pasji, że jeśli tylko pojawia się gdzieś np. operacja wejścia-wyjścia, mam do wyboru zaniechać takich bajerów jak strumienie (te z klasy Stream
, nie strumienie bajtów i znakowe), albo pisać kod brzydki jak strzępiel. Mam całe biblioteki, które rzucają określone wyjątki, i bardzo słusznie, że to robią, z którymi miałbym co zrobić? Wykastrować obsługę błędów?…
Gdyby to ode mnie zależało, interfejs Function
(i analogicznie cała reszta) zamiast tak:
interface Function<U, V> {
V apply(U u);
}
Wyglądałby tak:
interface Function<U, V, E extends Throwable> {
V apply(U u) throws E;
}
…I już mogę zrobić lambdę, która wywołuje Class.forName()
. O! Wiem, że w ten sposób implementacja może rzucić tylko jeden typ wyjątku, ale lepszy rydz niż nic. (BTW: Nie wiedzieliście, że można robić takie cosie z generycznymi deklaracjami wyjątków, prawda?)
Ogólne wrażenie: dobre i to.