Lambda výrazy v Javě

10. 3. 2014 10:04 zboj

Už déle neplatí, že C# je jen okopírovaná Java, a poslední verze Javy převzala od jiných jazyků tzv. lambda výrazy. Jenže trochu podivně.

V C jsou od pradávna pointry na funkci ( void (*funkce)()) a analogicky (nestandardní) bloky ( void (^funkce)()). V C++ jsou lambda výrazy také ( function<void()>). C# dtto.

A jaký typ má lambda výraz (např. obj -> { neco(obj); }) v Javě? Zde se situace poněkud komplikuje. Každý by asi předpokládal, že to bude objekt. Jenže v Oraclu si přidělali práci a zavedli tzv. funkční rozhraní. A nutí vás je používat. Že jsou některá předdefinovaná v java.util.function  je spíše špatná zpráva než dobrá, protože tam to prostě nepatří.

Dalším problémem jsou proměnné v lexikálním rozsahu platnosti. Překladač si vynutí, aby byly „final“ nebo „effectively final“. Důsledkem je, že proměnné použité v lambda výrazech nelze nijak modifikovat, a to ani vně (sic!) výrazu. Oficiální zdůvodnění je, že ve vícevláknových programech by to dělalo bordel. Jinými slovy – mnozí programátoři jsou nemyslící paka, tak jim to radši rovnou zakážeme. Rozumnější zdůvodnění by bylo (když už teda), že se tím zabrání anomálii vyskytující se v C# (jež se v tomto případě chová jako Javascript), kdy se například proměnná cyklu, v němž nějaký lambda výraz vzniká, „záhadně“ mění pod rukama. Už jsem viděl nesčetně vývojářů, jak se marně snaží najít tuto chybu (nebo „chybu“, on to tak Microsoft udělal schválně). Na pováženou je, že vysvětlení, proč se C# chová, jak se chová, nebyli schopni pochopit.

Jistě, i v C lze s lambda výrazy narazit, např. s následujícím kódem:

__block int p; int& q = p;

Proměnná q totiž po nějaké době může ukazovat na náhodné místo v paměti (přesněji: změní se adresa proměnné p a q ukazuje stále na stejné místo, kde už nic není nebo tam jsou jiná data). Zdá se, že nejdokonaleji si s výše uvedeným problémem poradilo C++, kde je na odpovědnosti programátora, aby určil, jak se bude proměnná v lambda výrazu chovat: buď si udělá kopii (přístup Javy a defaultně C), nebo použije referenci (přístup Javascriptu a C#).

Shrnuto a podtrženo, lambda výrazy v Javě jsou jen syntaktický cukr. Při použití ve spojení s knihovními metodami (nabízí se hlavně u kolekcí) člověk nemusí vytvářet anonymní třídy, takže ušetří trochu kódu. Zde mají smysl. Pokud je ale nutné stav lambda výrazu nějak měnit, je stále nutné vytvářet si tzv. „holder“, a pak je přehlednější použít anonymní třídu, než mít holder a k tomu ještě funkční rozhraní. Třeba to Oracle v Javě 9 napraví.

Sdílet