A Java8 – jobban mondva a Lambda csomag – lehetőséget ad arra, hogy interfészekbe lehessen tenni kódrészleteket, amelyeknek első nekifutásra nem sok értelme van, hiszen erre eddig is lehetőséget kaptunk az absztrakt osztályokon keresztül... a többszörös öröklődésre pedig nincs akkora igény, hogy komolyan el kellene gondolkodni rajta a nyelvet módosító fejlesztőknek. Ha jobban beleássuk magunkat a Lambda rejtelmeibe, akkor láthatunk egy logikus folyamatot, amely megmagyarázza a Virtual Extension Method néven nevezett nyelvi eszköz szükségességét. Írjunk egy rövid programot, amely létrehoz egy listát, benne számokkal 0 és 9 között:
public class LambdaRun { public static void main(String[] args) { java.util.List<Integer> numbers = new java.util.ArrayList<Integer>(java.util.Arrays.asList(new Integer[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); } }
A feladat legyen egyszerű: írassuk ki a listában található számokat... a hagyományos módszer szerint valami ilyesmit írnánk a program végére:
for(Integer number : numbers) System.out.println(number);
Az új módszer se sokkal kevesebb, a List interfész kapott egy új – forEach nevű – metódust, amelybe tudunk írni Lambda kifejezéseket:
numbers.forEach(number -> {System.out.println(number);});
Az ördög a részletekben rejlik, nézzünk bele a List interfész forráskódjába, de ott nem találunk benne forEach metódust, amelyet a Collection interfészben se találunk (amelyből a List származik), mivel ez a metódus az Iterable interfészben van, amely a Collection egyik őse, de lássuk gyorsan az alapértelmezett metódust, amelyben egy delegate metódustörzs szerepel:
void forEach(Block<? super T> block) default { Iterables.forEach(this, block); }
Ha tovább nyomozunk, akkor megtaláljuk a konkrét implementációt az Iterables osztályban, amely tulajdonképpen végrehajtja a Lambda blokkot:
public static <T> Iterable<T> forEach(final Iterable<? extends T> iterable, final Block<? super T> block) { Objects.requireNonNull(iterable); Objects.requireNonNull(block); for (T each : iterable) { block.apply(each); } return (Iterable<T>) iterable; }
Kerekedik a dolog... ezért kell a default metódustörzs az interfészekbe, mert a Lambda expression miatt az összes alapvető interfészbe fel kell venni a fentihez hasonló új metódusokat, amelyeknek működnie kell a régi implementációkkal – hiszen a platform alapvető interfészeit sok helyen sokszor implementáltuk saját osztályokban! Csak úgy lehet ezt megoldani, ha az interfészekben lehetőség van olyan metódusokat felvenni, amelyeket nem kell implementálni, a kialakult megoldás pedig a platform egészében használható eszköz lesz... csak szoknunk és tanulnunk kell még ezt a megoldást.
18 Comments
Anonymous
Erdekes ez a lambda dolog. Bar ugy gondolom, hogy ezt is meg lehetett volna oldani sima callback fuggvennyel, szoval leginkabb csak azert csinaltak, hogy elmondhassak, hogy java-ban is van Lambda.
Foleg, mert a fenti peldaban a lamba modszer 1 karakterrel sem rovidebb mint a hagyomanyos modszer...
Auth Gábor AUTHOR
Érdekesnek érdekes... egyelőre nem tudom hova tenni, hogy lesz ilyen a Java nyelvben. A callback függvény is működne, de ahhoz kellene egy metódus, ami okán már kesze-kusza lesz a kód. Mondjuk így is meg kell szokni, mert nem áll rá a szemem ezekre a kis beágyazott izékre...
Nyilván a példa kicsit erőltetett és lehetne találni egy csomó olyan példát, amely rövidebb és átláthatóbb kódot eredményez, de most még azt mondja a belső hang, hogy a "változás rossz!"
Zs
Azért ha callback függvénnyel csinálnád, akkor valami ilyesmit irnál, hogy:
numbers.forEach(new Callable<Integer>(){ public void call(Integer number) {System.out.println(number);});
ennél azért jobb ez lambda kifejezés.
Anonymous
Ez igaz. Viszont ebben nincs semmi "magical", minden ott van a kodban. Barki, aki tud java-ul, az erti.
Ugy erzem, hogy ezzel a lambda dologgal a funkcionalis megkozelitest probaljak eroltetni, de (egyelore) nem erzem ugy, hogy a java (szeru) nyelvekben ez olyan pozitivum lenne.
LSM Hideki
Ha használtál valaha C#
ot, akkor nagyon hamar elkezded hiányolni a JAVAból.Ezt már nagyon régen bele kellett volna tenni, de valszeg akkor ma sem lenne még kint a Java 7.
Auth Gábor AUTHOR
Én alapvetően szerver oldalon dolgozom, és nem nagyon tudom elképzelni, hogy mire tudnám értelmesen használni... de biztos bennem van a hiba...
LSM Hideki
Ez egy pici C# kód, ahol kiszűrtem a súlynál null elemeket tartozóakat, majd rendeztem elsődlegesen a súly szerint, az azonos súlyúakat név szerint. Nem szép minta és pontatlan is (
), de a lényege, hogy három sorban sikerült ezt a célt elérni. Java8-tól ez is működni fog.
var myLittleList = new List<MyElement>();
myLittleList.AddRange(_myBigList.Where(e => e.myWeight != null));
myLittleList.Sort((m1, m2) => (m2.myProperty.CompareTo(m1.myWeight)*100+m1.name.CompareTo(m2.name)));
Auth Gábor AUTHOR
A lista honnan jön? Ha adatforrásból (és nyilván adatforrásból), akkor ezt már ott meg tudom csinálni...
Anonymous
johet a kliens oldalrol felhasznaloi imputbol is, ahol nem hiba, ha kitorlik a suly erteket a mezobol, tehat nem validalhatsz ilyet
Anonymous
Boobek voltam
Auth Gábor AUTHOR
Akkor meg a JS szűri ki...
Értem én, hogy ez egy must have desktop feature, de a Java nem volt és már nem lesz soha desktop platform, Android-on a GUI megoldja a validációt, a service rétegnek meg ott a SQLite.
Alapvetően erre reagáltam.
LSM Hideki
Mondok én is egy példát: mondjuk a súly helyett az utolsó havi fizetés szerepel. A listában pedig az idén a cégnél dolgozók szerepelnek, de mondjuk néhányan már távoztak a cégtÅl, és azoknak nincs fizetésük.
Tény, hogy le lehet kérni adatforrásból, de minek, ha ebben a második listában már megkaptuk, amit szerettünk volna.
Boobek altarnatívája is egy lehetséges út.
Auth Gábor AUTHOR
Tehát ismét a kliens oldalon csinálunk valamit az adatokkal valamilyen esemény hatására (például táblázat rendezése, szűrése)...
C# van kliens oldalon, ott van értelme. Java elenyésző van kliens oldalon, és nem mernéd szerintem kijelenteni, hogy csak és kizárólag azért nincs, mert hiányzik a nyelvből a lambda expression és néhány más dolog, ami C# esetén rendelkezésre áll.
Alapvetően attól tartok, hogy a lambda expression és a LINQ agyatlan használata is átszivárog, amikor az adatbázistól elkér az ember mindent, de tényleg mindent, leutaztatja a kliensre és ott ezekkel az eszközökkel keresgél benne, aztán csodálkozik, hogy ami működött 100 rekordra, az nem megy százmillió rekorddal. Nem egy példát láttam erre sajnos.
A szűrést és a rendezést csinálja az adatforrás, a service réteg oldja meg a cache funkciókat, a kliens csak mutassa meg, amit kell... szerintem...
Zs
Nem értem mi a köze a szerver/kliens oldalnak egy nyelvi ficsőrhöz ? a for ciklust hol szoktad csak használni?
Auth Gábor AUTHOR
Errefelé nem szokás a teljes adathalmazt letolni a kliensnek, hogy majd ott kiválogatja, ami neki kell...
Nem vagyok konzervatív, szeretem az új dolgokat, de szerintem ez a lambda expression kissé túl van lihegve.
Zs
Tehát Te mindig olyan pontos SQL lekérdezéseket irsz, hogy a visszakapott eredményen már semmi módositásra, csoportositásra, rendezésra, összekapcsolásra nincs szükség, csak elkészited a List<MyOrderLineResult> objektumodat a ResultSet-edből, és azt már egy az egyben el lehet küldeni a kliensnek ?
Ez a legegyszerűbb esetekben még hihető is, de én inkább eddig azt láttam, hogy van egy objektum model, ami az adatbázisból jön, s abból készülnek el a DTO-k, néha nem triviális adat masszirozással.
Én a kevesebb (érthető) kód, kevesebb hiba, mantrában hiszek. Nyilván az érthetőség fontos szempont, ha valaki látott már K-t
Auth Gábor AUTHOR
Ott igyekszem megoldani az adatok szűrését, ahol a leginkább célszerű. Az esetek igen nagy részében ez az adatbázis. Ha utólag rendezni kell, akkor arra is teljesen kész megoldások vannak, nem feltétlen egy lambda expression hiányzik a boldogsághoz. Majd meglátjuk néhány év múlva.
Zs
Nyilván, ahol a legcélszerűbb, ott lehet használni lambdákat, függetlenül, hogy az valamilyen kontextusban "szerver" vagy "kliens" oldalinak számít. Többnyire tömörít a kódon, szvsz.