7,540
edits
Changes
→Funkcionális Interfész alapok
=Funkcionális Interfész alapok=
==Mire jó a funkcionális interfész== A funkcionális interfészek használatával megszabadulhatunk az úgynevezett boilerplate (közhelyes) code használatától, vagyis nagyon egyszerűen, osztály példányosítás nélkül tudunk interfész metódus implementációkat készíteni.Ennek az egyik kulcs eszköze a JAVA8-ban bejött LAMBDA és úgynevezett metódus referencia, amire majd látunk bőven példát.Azt mondják hogy ez a java-ba valaha behozott legnagyobb újítás. TODO<br><br>A fent leírtakat persze persze anonymus osztályokkal is el lehet érni, csak többet kell hozzá írni.Pl: A Theread osztályban szokásos anonimusz osztályokat létrehozni a run() definiálására:<source lang="java"> public class AnonymousClassExample { public static void main(String[] args) { Thread thread = new Thread(){ public void run(){ System.out.println("Understanding what is an anonymous class"); } }; thread.start(); }}<br><br>Lambda kifejezéssel ugyan ez így írható le: <source lang="java">public class AnonymousClassExample { public static void main(String[] args) { Runnable runnable = () -> { System.out.println("Understanding functional interfaces"); }; runnable.run(); }}</source> <br><br> ==Hogyan működik==A funkcionális interfész egy olyan interfész, amiben csak egy darab absztrakt metódus van. (vagyis ami implementálandó). Ezt kidomborítandó, a '''@FunctionalInterface''' annotációval is el lehet látni az interfészt. Készítsünk egy egyedi String osztályt, amivel majd példálózunk (ez még nem kapcsolódik a funkcionális if-re)<source lang="java">public class MyString { private String input; public MyString(String input) { System.out.println("MyString consturctor is called with input:" + input); this.input = input; } public String getInput() { return input; } }</source><br><br>Tekintsük az alábbi funkcionális if-et, aminek egy absztrakt metódusa van, ami egy String-et vár, és egy MyString-et ad vissza. <source lang="java">@FunctionalInterfacepublic interface MyFunction { MyString process(String input);}</source> Nézzük át milyen módon lehet felhasználni ezt a funkcionális IF-et: ===Implementációs osztály példányosítása===A klasszikus használata egy interfésznek, ha készítünk egy osztályt, ami implementálja az absztrakt metódusokat, majd példányosítjuk az osztályt, és klasszikus módon használjuk. Ehhez nincs szükség funkcionális IF-re, vagyis hogy csak egy absztrakt metódusa legyen az interfésznek, mivel az implementációban explicit megmondjuk, hogy melyiket implementáljuk: <source lang="java">public class MyFunctionImpl implements MyFunction { @Override public MyString process(String input) { return new MyString(input); }}</source><br>Majd ezt így használhattuk: <source lang="java"> MyFunctionImpl2 myFunctionImpl2 = new MyFunctionImpl2(); MyString myString = myFunctionImpl2.process("MyInput"); System.out.println(myString.getInput());</source><br><br>===Anonimus osztály használata===Ahogy a bevezetőben is láttuk, bármilyen interfésznek lehet anonimus osztállyal definiálni a még nem definiált interfészeit az alábbi szintaxissal: <pre>new InterfaceName() { @Override public <implementálandó metódus definíció> {..}};</pre>Itt sincs szükség funkcionális interfészre, vagyis hogy csak egy absztrakt metódusa legyen az interfésznek, mert explicit megmondjuk hogy melyiket implementáljuk. <br>Gyakorlatban ez így néz ki az előbbi példára: <source lang="java"> MyFunction fm2 = new MyFunction() { @Override public MyString process(String input) { return new MyString(input); } }; fm2.process("input"); </source> ===Lambada kifejezés használata===A fenti anonimus osztály definíció egy sokkal rövidebb Lambda kifejezéssel kiváltható, de csak akkor ha a szóban forgó interfész egy '''funkcionális interfész''', vagyis pontosan egy darab absztrakt metódusa van. A Lambda kifejezés szintaxisa az alábbi: <pre>(arguments) -> {function body}</pre>Ahol az argomentumok az egy szem absztrakt metódus input paraméterei, a function bdoy-ban pedig leírjuk, hogy mit tegyen ezekkel az input paraméterekkel az implementáció. Az argumentumok típus nélküliek. * A paraméterek neve tetszőleges lehet, nem kell megegyezzen a funkcionális metódus paraméter neveivel. A sorrend viszont számít. A lényeg, hogy a body-ban az argumentumokban felsorolt nevekkel tudunk hivatkozni az input paraméterekre. * Ha az interfész input paraméter generikus, csak akkor fognak típust kapni, amikor meghívásra kerül a kódban explicit a funkcionális interfész implementált metódusa, vagyis kiderül, hogy mivel etetjük me az implementált metódust. * Ha a visszatérési érték is generikus az implementálandó metódusban, akkor annak vagy implicit következik a típusa a lambda kifejezésben használt visszatérési értéktől, vagy ha az függ az input paraméter típusától, akkor majd a metódus meghívásakor fog típust kapni a metódusba használt input paraméterek típusától függően. * Ha a funkcionális interfész nem generikus, akkor az interfész input és return értékéből már explicit következik a lambdában használt paraméterek és visszatérési értékék típusa. <br><br>Nézzünk egy konkrét példát. A 'MyFunction' egy funkcionális interfész, mert pontosan egy absztrakt metódusa van: 'MyString process(String)'. Tehát a lambda kifejezéssel ennek a 'process(..)' absztrakt metódusnak az implementációját kell definiálni: <source lang="java"> MyFunction fm = var1 -> { return new MyString(var1); }; fm.process("input");</source>Láthatjuk, hogy a 'MyFunction' egy szem absztrakt metódusát egy lambda kifejezéssel definiáltuk, amivel egyben el is készítettük a MyFunction implementációjának példányosítását. A 'MyFunction'-enk egy darab absztrakt metódusa van, az alábbi szignatúrával: 'MyString process(String)', ebből következik, hogy a '->' elé pontosan egy változó nevet kell kitaláljunk, ami bármi lehet, lényeg, hogy a body-ban ezzel a névvel tudunk rá hivatkozni. Mi itt 'var1' nevet találtunk ki, aminek a típusa 'String', mivel a 'process(..) metódusban egy darab String input paraméter van. Ha a 'process(..)' generikus inputtal rendelkezne, akkor a lambada-ban a 'var1' nek a típusa csak használat közben derülne ki, majd később erre is látunk példát. Tehát az alábbi két sor egyenlő: <pre>MyFunction fm = var1 -> { return new MyString(var1); }; fm.process("input"); EGYENLŐMyFunction fm = new MyFunctionImpl();fm.process("input");</pre> Mit fontos itt látni: A lambda definiálásának a sorába még nem fut le a lambda törzsében megadott kód, vagyis ez: return new MyString(var1); JAVA 8-tól kezdve a funkcionális IF definíciója az úgynevezett Lambda kifejezéssel is leírható, anélkül, hogy bármilyen osztályt létre kéne hozni. <br><br>
=Lambda kifejezések=