Changes

Java Funkcionális interfész & Lambda

28,638 bytes added, 15:03, 19 August 2022
Hogyan működik
=Java λ=
<< [[ Java ]]
=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.
<br>
<br>
A fent leírtakat persze persze anonymous osztályokkal is el lehet érni, csak többet kell hozzá írni. Pl: A Theread osztályban szokásos anonymous 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();
}
}
</source>
<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">
@FunctionalInterface
public interface MyFunction {
 
MyString process(String input);
}
</source>
 
Egy funkcionális interfész implementációjának a megadására az alábbi 5 lehetőségünk van:
* Hagyományos implementációs osztály példányosítása
* anonymous osztály használata
* Lambda kifejezés használata
* Metódus referencia használata: lásd [[#Java_Method_Reference_and_Constructor_Reference]]
* Konstruktor referencia használata lásd [[#Java_Method_Reference_and_Constructor_Reference]]
<br>
<br>
===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>
===Anonymous osztály használata===
Ahogy a bevezetőben is láttuk, bármilyen interfésznek lehet anonymous 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>
 
<br>
<br>
===Lambada kifejezés használata===
A fenti anonymous 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 argumentumok 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 típusuk implicit következik a felhasználási módból, lásd lentebb.
* 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 a generikus értékeket definiáljuk az interfész változó létrehozásánál.
* Ha a visszatérési érték is generikus az implementálandó metódusban, akkor annak a típusa is csak az adott felhasználásból fog következni.
* 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 -> {
System.out.println("inside lambda body");
return new MyString(var1);
};
System.out.println("Start");
fm.process("input");
System.out.println("End");
</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.
<br>
Fontos, hogy a 'var1'-nek a globálisan semmi jelentése nincs, nem létező változó a fenti kódot futtató kontextusban. Ezt úgy kell elképzelni, mint egy metódus leírója, ami majd akkor kerül meghívásra csak, ha meghíjuk programozottan a 'process(..)' metódust, csak akkor fog feltöltődni értékekkel. <br>
Az stdout-ra a következő kerül:
<pre>
Start
inside lambda body
End
</pre>
 
<br>
<br>
 
Tehát az alábbi két sor egyenlő:
<source lang="java">
MyFunction fm = var1 -> { return new MyString(var1); };
fm.process("input");
EGYENLŐ
MyFunction fm = new MyFunctionImpl();
fm.process("input");
</source>
 
!!!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); !!!
 
 
<br>
<br>
===Lambda generikus interfésszel===
Tegyük fel, hogy adott az alábbi generikus, funkcionális interfész. A funkcionális (absztrakt) metódusa egy T-t és H-t vár és R-t gyárt belőle. Azt hogy hogyan, és hogy mi a T, H és mi az R, az implementációra van bízva.
<source lang="java">
@FunctionalInterface
public interface MyFunctionGeneric<R, T, H> {
R doIt(T var, H var2);
}
 
</source>
<br>
Definiáljuk a 'MyFunctionGeneric' interfész absztrakt metódusát egy lambda kifejezéssel. A 'MyFunctionGeneric fm' változó létrehozásakor már definiálni kell az R és T és H típusokat, ebből fog következni, hogy a lambada-ban használt paraméter listának milyen értékei vannak, hogy mi kell legyen a visszatérési érték.
<br>
<br>
Az alábbi példában létrehoztuk az fm3 'MyFunctionGeneric' változót, és itt meghatároztuk, hogy a visszatérési érték Integer, és a bement két String. A lambda kifejezés input paramétereiben már két értéket kell megadni, ezért zárójelet kell alkalmazni.
<source lang="java">
MyFunctionGeneric<Integer, String, String> fm3 = (myInput1, myInput2) -> myInput1.length();
fm3.doIt("my input", "my input2");
</source>
Az input argumentumoknak megint csak bármilyen nevet megadhatunk, a fenti kódot futtató kontextusban nem léteznek, ez csak egy metódus tervrajz, nem metódus futtatás. A 'myInput1' és 2-nek a típusa következik az fm3-ban megadott generikus típusokból, vagyis mind a kettő String. A visszatérési érték pedig Integer. Ha a lambda body-ban nem akarunk több soros művelet végrehajtani, csak egy 'return (one line logic)' sort tartalmazna, akkor a 'return' kulcsszó elhagyható. Fontos megint csak látni, hogy a doIt(..) metódus hívás hatására fog csak lefutni a lambda-ban definiált kód.
<br>
<br>
 
=Lambda használati példák =
==Tipikus használati minták==
 
..TODO: mindig egy metódus beljesében van, és a metódus lambda-t vár input paraméterben, és az osztály belsejében lévő változókkal szokott operálni. ...
 
<br><br>
==List-forEach()==
<br>
Ez miért működik?
<br><br>
A List a java.lang.Iterale osztály leszármazottja. Ebben van egy default forEach(..) nevű metódus implementáció a forEach-reami egy úgynevezett funkcionális interfész implementációt vár input paraméterben, vagyis egy '''Consumer''' implementációt.
<source lang="java">
package java.lang;
import java.util.function.Function;
public class MyLambadClass MyClassUsingFunctionalif {
private String variable1;
public MyLambadClassMyClassUsingFunctionalif(String a) {
this.variable1 = a;
}
<source lang="java">
MyLambadClass myLambadClass MyClassUsingFunctionalif MyClassUsingFunctionalif = new MyLambadClassMyClassUsingFunctionalif("adam");
MyFunctionImpl myFunction = new MyFunctionImpl();
myLambadClassMyClassUsingFunctionalif.processVariable(myFunction);
</source>
<br>
public static void main(String[] args) {
MyLambadClass myLambadClass MyClassUsingFunctionalif MyClassUsingFunctionalif = new MyLambadClassMyClassUsingFunctionalif("adam"); myLambadClassMyClassUsingFunctionalif.processVariable(var1 -> { return Integer.valueOf(var1.length()); });
}
Ami nem csinál mást, mint visszaadja a kapott string hosszát. Kívülről nézve a var1 nem értelmezhető, az mindig a lamdát futtató osztály egy osztály változója, kívülről nem megadható.
{{warning|Itt a 'var1' nem kívülről jövő, a metódus meghívásakor előállt paraméter! Ez a 'MyLambadClassMyClassUsingFunctionalif' belső változója, vagyis ide kívülről nem tudunk változót betolni a metódus meghívásakor }}
Tehát, ahogy ezt majd látni fogjuk a CompletionSage-nél, a 'FunctionalInterface'-t futtató osztályt kell előre feltölteni minden olyan változóval, amire szükség van a lamda kifejezés futtatására. A fenti példákban ez egyrészt a 'MyLambadClassMyClassUsingFunctionalif', vagy az első példában a ArrayList osztályok.
* A 'MyLambadClassMyClassUsingFunctionalif' konstruktorában adtuk át azt a string-et, amit a 'processVariable' feldolgoz, attól függetlenül, hogy milyen lamda kifejezéssel implementáljuk a funkcionális interfészét.
* Az ArrayList pedig belső változóiban tárolja
 
<hr>
<br>
<br>
 
=Java Method Reference and Constructor Reference=
https://www.amitph.com/java-method-and-constructor-reference/
==Method Reference==
A metódus referencia (akár statikus akár nem) arra jó, hogy egy meglévő osztály egy metódusát át tudjuk adni a funkcionális IF definíciót váró metódusnak ahelyett hogy definiálnánk helyben lambda belsejét. Azonban ez nem egyezik meg a Funkcionális interfész implementációját tartalmazó osztály használatával. Ha metódus referenciát használunk, akkor a funkcionális IF implementáció helyére megadott osztály és metódus nem kell hogy implementálja a funkcionális interfész funkcionális metódusát, elég ha megfelel a karakterisztikája az éppen használt funkcionális if kifejezésnek, vagyis az elvárt paraméterekkel rendelkezik, és a visszatérési érték típusa is megfelel a várt funkcionális if-nek kifejezésnek.
 
Nézzük ismét az alábbi példa class-t, aminek van egy metódusa, ami egy funkcionális interfészt vár (processVariable). Itt egy beépített funkcionális IF-et használunk megint
<source lang="java">
package hu.otp.auth.loginHelper.util;
import java.util.function.Function;
 
public class MyClassUsingFunctionalif {
 
private String variable1;
 
public MyClassUsingFunctionalif(String a) {
this.variable1 = a;
}
 
public void processVariable(Function<String, Integer> fn) {
 
Integer length = fn.apply(variable1);
System.out.println("Value given by function IF impl: "+ length.toString());
}
}
</source>
<br>
 
Emlékeztetőül, a java gyári Function nevű funkcionális IF implementációja így néz ki:
<source lang="java">
package java.util.function;
 
@FunctionalInterface
public interface Function<T, R> {
R apply(T var1);
...
</source>
Láthatjuk, hogy egy implementálandó metódusa van, ezt lehet egy lambda in line IF-el megadni, vagy akár egy implementációs osztállyal definiálni.
 
Tehát, normál esetben ez működne:
<source lang="java">
public class MyFunctionImpl implements Function<String, Integer> {
@Override
public Integer apply(String s) {
return s.length();
}
}
</source>
</br>
Majd ha meghajtjuk a metódust ezzel az implementációval:
<source lang="java">
MyClassUsingFunctionalif MyClassUsingFunctionalif = new MyClassUsingFunctionalif("process_this_during_functional_if_call");
MyClassUsingFunctionalif.processVariable(new MyFunctionImpl());
</source>
Akkor a konzolon meg fog jelenni hogy: "38"
 
<br>
Mert hogy: <br>
A '''processVariable()''' metódus belsejében azt mondtuk meg, hogy fogja meg a konstruktorban kapott változót, adja azt át a '''Function''' funkcionális if-et implementáló osztály '''apply''' metódusának és amit az visszaad, azt írja ki a képernyőre. A '''MyFunctionImpl''' -ban rögzítettük, hogy a generikus IF milyen típusokkal fog rendelkezni (String megy be, és Integer jön ki). Azt hogy ezt hogy állítja elő az implementáció, erről a '''processVariable()''' metódusban semmit sem tudunk, teljesen az implementációra van bízva.
===Rövidítés===Na de mit jelent ez a sor<br>Persze, ezt lambdával is megadhattuk volna, akkor így nézne ki:
<source lang="java">
MyClassUsingFunctionalif MyClassUsingFunctionalif = new MyClassUsingFunctionalif("process_this_during_functional_if_call"); MyClassUsingFunctionalif.thenComposeprocessVariable(this::method3input -> {return input.length();}); ---> 38
</source>
Itt inline, lambda definíciót használtunk. A generikus változói a '''Function''' funkcionális interfésznek (T és R) explicit lett definiálva azáltal, hogy a '''processVariable(..)''' belsejében a funkcionális IF (apply) String paraméterrel van meghívva és Integer-t vár vissza.
Ez csak <br><br>Na már most, ezt lehet nagyban egyszerűsíteni bármilyen olyan Osztály statikus vagy nem statikus metódusával, ami String-et vár és Integer-t ad vissza. Erre szolgála '''::''' operátor. Statikus metódus használata esetén nem kell példányosítani az osztályt, ennyi a különbség. Ami a lényeg: '''ezen osztálynak nem kell implementálnia a várt funkcionális interfész itt használt metódusát!!''' <source lang="java"> MyClassUsingFunctionalif MyClassUsingFunctionalif = new MyClassUsingFunctionalif("process_this_during_functional_if_call"); MyClassUsingFunctionalif.processVariable(String::length); ----> 38</source>Ezzel azt mondjuk meg, hogy az '''R pply(T)''' helyett inkább hívd meg az '''Integer Sring.length(String input)''' metódust.  <br>VAGY ha nem statikus: <br><br>Tegyük fel hogy van egy rövidítésilyen osztályom, ami nem implementál semmilyen IF-et: <source lang="java">public class MyString { public Integer getLengthOfImput(String input) { return input.length(); }}</source> Akkor a '''getLengthOfImput(..)''' is átadható példányosítás után a '''::'' operátorral, mint behelyettesítés: <source lang="java"> MyClassUsingFunctionalif MyClassUsingFunctionalif = new MyClassUsingFunctionalif("process_this_during_functional_if_call"); MyString myString = new MyString(); MyClassUsingFunctionalif.processVariable(myString::getLengthOfImput); ---> 38</source>Vagyis ahelyett hogy meghívná a funkcionális IF '''R apply(T)''' metódusát, inkább meghívja majd a '''getLengthOfImput''' metódust. Egy statikus <br><br> ==Constructor Reference== A konstruktor referencia egy speciális Metódus referencia, ami csavar még egyet a fenti metódus referenciakoncepción, itt már nagyon kell figyelni. <br>Ugyebár minden funkcionális IF visszaad egy objektumot, és vagy kap vagy nem input paramétereket. A lényeg, hogy egy előre nem definiált módon gyártson le egy olyan objektumot, amit visszaad akkor mikor meghívjuk a funkcionális metódusát, az előbbi példában az '''R apply(T)''' metódust. Ahelyett Ahol kap egy T-t és tök mindegy hogy megadnánk hogy, csak gyártson le egy '''R''' osztály példányt. (ez tökre hajaz egy konstruktor definíciójára, na nézzük csak meg:) )   Tekintsük az alábbi funkcionális IF-et: <source lang="java">@FunctionalInterfacepublic interface MyFunction {  MyString process(String input);}</source> Bármi is legyen az implementáció (impl osztállyal vagy inline lambda kifejezéssel) a lényeg, hogy előállítson a MyString objektumot. <br>A konstruktor referencia lehetővé teszi, hogy a funkcionális IF implementációtegy osztály konstruktorával helyettesítsük be. Vagyis a funkcionális metódus visszatérési objektumát úgy állítja elő, hogy szimplán példányosítja a megadott osztályt, jelen esetben a MyString-et. Az osztálynak rendelkeznie kell egy olyan konstruktorral, ami a funkcionális metódus input paramétereivel megegyezik. A fenti esetben tehát akkor fogjuk tudni a MyString -et konstruktor referenciában használni, ha rendelkezik egy olyan konstruktorral, ami String-et vár.  pl:<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>  A konstruktor referenciát ::new val kel megadni, azt mondjuk megvagy a '''::'' operátor után a '''new''' kulcsszót kell írni. Egy lehetséges példa: Tegyük fel, hogy ez van egy java osztályunk, amiben van egy metódus, ami felhasználja a '''MyFunction''' funkcionális if-et:<source lang="java">public class MyClassUsingFunctionalif {  private String variable1;  public MyClassUsingFunctionalif(String a) { System.out.println("MyClassUsingFunctionalif consturctor is called with input:" + a); this.variable1 = a; }  public void processVariable2(MyFunction mf) {  System.out.println("MyClassUsingFunctionalif.processVariable2 START"); MyString myString = mf.process(variable1);  System.out.println("MyString instance created by function if call: "+ myString.getInput());  System.out.println("MyClassUsingFunctionalif.processVariable2 END"); }}</source>Vagyis vagy a függvény végzi el azt MyFunction implementálásával vagy egy inline lambda kifejezéssel gyártsunk olyan kódot, ami a feladatotsaját osztály String változóját (varibale1) átalakítja egy MyString osztállyá, amit az ő aztán fel tud használni a metódusban (jelen esetben kiíratjuk).   És ahelyett hogy:* Implementációt készítünk a MyFunction-hez* lambda kifejezésben adjuk meg inline implementációnk végzett volna el* Metódus referenciát használunk egy olyan osztály metódusával, ami String-et vár és gyárt belőle egy MyString-et Simán megfogjuk a MyString osztályt és a konstruktorára hivatkozunk a '''MyClassUsingFunctionalif. processVariable2(..)''' metódus használatakor. <source lang="java"> MyClassUsingFunctionalif myClassUsingFunctionalif = new MyClassUsingFunctionalif("process_this_during_functional_if_call"); myClassUsingFunctionalif.processVariable2(MyString::new);</source> És ez kerül az output-ra:
<pre>
<Melyik osztály>MyClassUsingFunctionalif consturctor is called with input:process_this_during_functional_if_callMyClassUsingFunctionalif.processVariable2 STARTMyString consturctor is called with input:<melyik metódusa>process_this_during_functional_if_callMyString instance created by function if call: process_this_during_functional_if_callMyClassUsingFunctionalif.processVariable2 END
</pre>
Ezt <br> Tehát az egész '''MyString myString = mf.process(variable1);''' hívást helyettesítette a MyString konstruktor meghívásával. Láthatjuk a log sorrendből is, hogy ebben a sorban: <pre>myClassUsingFunctionalif.processVariable2(MyString::new);</pre>Még nem példányosodik a MyString osztály. Csak akkor mikor a funkcionális IF meghívásra kerül, de azt simán behelyettesíti egy olyan konstruktor hívással, ami ugyan azokat a paramétereket várja, és a '''::new'''-val megadott osztálynak semmi köze a funkcionális IF-hez (MyFunction), nem implementálja azt.  <br><br> =Functional interface in ENUM constructors=* Ehhez elsőként ajánlott elolvasni az ENUM alapokat: [[ Java Enum]]* Második lépésnek pedig ezt: [[ Java_Lambda#Constructor_Reference ]] ==Alapok áttekintése==A [[ Java Enum]] cikkben láthattuk, hogy az enumoknak is lehet alkalmaznikonstruktort, ha amúgy metódusokat és osztály változókat készíteni. Ismétlés képen itt egy enum, aminek van egy int változója, és ezt a konstruktor inicializálja, vagyis minden ENUM érték esetében meg kell adni egy 'int' számot is csak csak metódus hívást írtunk volna a body-ba, amit el fog rakni az enum példányba:
<source lang="java">
.thenComposepublic enum AdamTest {  LALI(var2 -> method32), ADAM(var23) ;  private int enumInt;  private AdamTest(int a){ this.enumInt=a; }}
</source>
 <hrbr><br>Ha olyan osztály változót készítünk az ENUM-nak, ami egy funkcionális interfész, és ezt szintén a konstruktorral inicializáljuk, akkor minden egyes ENUM érték esetében a konstruktorban meg kell adni ennek a funkcionális interfésznek a definícióját.  Tegyük fel, hogy adott az alábbi osztály, ami egy saját String változat: <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>Tegyük fel, hogy adott az alábbi funkcionális IF:<source lang="java">@FunctionalInterfacepublic interface MyFunction { MyString process(String input);}</source>Itt a 'process(..)' implementáció egy string-et vár és egy MyString-et ad vissza.  <br>Tekintsük az alábbi ENUM-ot. Az enum-nak van egy MyFunction típusú funkcionális if osztály változója, amit a konstruktor inicializál. Tehát minden enum értéknek meg kell adni egy MyFnction implementációt. <source lang="java">public enum MyEnum2 {  ENUMV1(..<MyFunction implementáció>...), ;  private final MyFunction function;  private MyEnum2(MyFunction function) {  System.out.println("ENUM constructor is called with input"); this.function = function; }  public MyFunction getFunction() { return function; }}</source><br>Láttuk már, hogy elvi szinten egy funkcionális if megadására 4 lehetőségünk van, azonban itt most nem használhatjuk mindent: * Implementációt készítünk a MyFunction-hez: Ez nem fog működni, mert az enum példányosításakor statikus kontextusban vagyunk, nincs hol példányosítsuk a funkcionális if implementációnkat, nem olyan mint amikor enum kívül, normál class metódusban használjuk. * lambda kifejezésben adjuk meg inline* Metódus referenciát használunk egy olyan osztály metódusával, ami String-et vár és gyárt belőle egy MyString-et* Konstruktor referenciát használunk    ===lambda kifejezésben adjuk meg inline===Az vetődhet fel kapásból bennünk, hogy az itt lambda-val definiált funkcionális if implementáció (vagyis a 'MyString process(String) impl') mikor fog lefutni, ki fogja meghívni, és mi lesz az a String érték amin dolgozni fog. A korábbi példákban mindig volt egy akármilyen metódus, ami várta paraméterül a funkcionális if-et, majd meghívta rajta a funkcionális metódusát (jelen esetben a process(Stringt)-et), úgy, hogy az a bizonyos metódus töltötte fel az összes input paraméterrel a funkcionális if hívást. Pszeudó kóddal ez elvi szinten így néz ki amiről itt beszélek: <source lang="java">class MyClassUsingFunctionIF {  void myMethodUsingFunctionIF(MyFunction mf) {  String inputForFuncion = "this is the input"; MyString result = mf.process(inputForFunction); }}</source>Itt a 'myMethodUsingFunctionIF(..)' meghívásával tudjuk implicit lefuttatni a funkcionális if implementációt. A process(..) input paraméterét saját belső változójából tölti fel.   ENUM konstruktor referencia estében el kell kérni az enum-tol a konstruktorban inicializált funkcionális függvényt, és meg kell hívni a funkcionális interfészét úgy hogy feltöltjük azt a megfelelő paraméterekkel. Nézzük ezt részletesen. Az alábbi példában az enum konstruktorában egy 'MyFunction' funkcionális interfészt kell átadni, amit egy inline lambda kifejezéssel definiáltunk (mármint a process metódusát). <source lang="java">public enum MyEnum2 {  ENUMV1(var1 -> { System.out.println("ENUM lambda is called with input: " + var1); return new MyString(var1); }), ;</source> {note||Az itt megadott kód ugyebár nem fog lefutni a a MyEnum2 példányosításakor. A példányosításkor a konstruktorban ezzel a lambda kifejezéssel csak megadtuk a MyFunction if tervrajzát, vagyis hogy a MyFunction osztály változó rakjon bele egy olyan "virtuális" implementációs példányt, amiben az itt megadott 'MyString process(String)' implementáció található. }}  Ahhoz hogy a 'process(String)'-et implementáló lambda kifejezésünk lefusson, elsőként példányosítani kell az ENUM-ot a megfelelő típusra, arra, ami a lambda kifejezést tartalmazza, ami nem más mint az '''ENUMV1''' (lehetne több értéke is az enum-nak, és mindegyikben más és más implementációt is megadhattunk volna). Majd a példányosított enmum-tól a getter-el el kell kérni a funkcionális IF osztály változót, aminek meg kell hívni a process(..) metódusát, úgy hogy megadjuk az if-en kötelező egy darab String input paramétert.  <source lang="java"> System.out.println("START");   MyEnum2 myEnum21 = MyEnum2.ENUMV1; System.out.println("Enum was created"); MyString result = myEnum21.getFunction().process("process input"); <<---- itt fut csak le a konstruktorban megadott lambda</source>Fontos, hogy az ENUM példányosításakor tudunk semmit "kívülről" megadni, tehát, nem lehet így példányosítani az ENUM-ot: <pre>MyEnum2.ENUMV1(..constructor values...); <------WRONG</pre> Nézzük meg futási sorrendet, ezt logolja az stdout-ra. Látható, hogy a lambda-ba megadott kifejezés csak a 'process(..)' metódus meghívásakor futott le. A lambda kifejezés tartalmazott egy 'MyString' példányosítást, az futott le utoljára. <pre>STARTENUM constructor is called with inputEnum was createdENUM lambda is called with input: process inputMyString consturctor is called with input:process input</pre><br><br> ===Metódus referenciát használunk===Metódus referenciát használunk egy olyan osztály metódusával, ami String-et vár és gyárt belőle egy MyString-et:  
<br>
<br>
=Java Method Reference and Constructor Reference=
https://www.amitph.com/java-method-and-constructor-reference/
==Method Reference==
===Konstruktor referenciát használunk===
Emlékezzünk rá, hogy funkcionális IF definíciókat meg kelhet adni azon osztály konstruktor referenciájával is, amikkel a funkcionális metódus visszatér, abban az esetben ha ez az osztály rendelkezik olyan konstruktorral, aminek a paraméterei megegyeznek a funkcionális metódus paramétereivel.
 
Pl. ha adott a már ismert Funkcionális if:
<source lang="java">
@FunctionalInterface
public interface MyFunction {
MyString process(String input);
}
</sring>
Akkor a funkcionális if definiálható a '''MyString::new''' konstruktor referenciával, ha rendelkezik String paraméterű konstruktorral:
<source lang="java">
public class MyString {
 
private String input;
 
public MyString(String input) { <<<<---------- ez itt a lényeg!!!!
System.out.println("MyString consturctor is called with input:" + input);
this.input = input;
}
 
public String getInput() {
return input;
}
}
</source>
 
<br>
Nézzük a már ismert java ENUM-ot, ezt egészítsük ki egy új értékkel: '''ENUMV2''', ahol a funkcionális if-et a konstruktorban a 'MyString' konstruktor referenciájával adjuk meg:
<source lang="java">
public enum MyEnum2 {
ENUMV1(var1 -> {
System.out.println("ENUM lambda is called with input: " + var1);
return new MyString(var1);
}),
ENUMV2(MyString::new) <<<-------------- ez itt a lényeg!!!
;
private final MyFunction function;
 
private MyEnum2(MyFunction function) {
 
System.out.println("ENUM constructor is called with input");
this.function = function;
}
 
public MyFunction getFunction() {
return function;
}
}
</source>
 
Ha példányosítjuk az enum-ot a ENUMV2 értékkel, majd azon meghívjuk a funkcionális if process(..) metódusát, akkor fog lefutni a MyString konstruktora:
<source lang="java">
System.out.println("START");
MyEnum2 myEnum22 = MyEnum2.ENUMV2;
System.out.println("Enum was created");
myEnum22.getFunction().process("prcoess again");
</source>
 
<br>
Az output az alábbi lesz:
<pre>
START
Enum was created
MyString consturctor is called with input:prcoess again
</pre>
<br>
<br>
==Constructor ReferenceMire használható (komplex példa)==<br><br>
=Stream=
https://stackify.com/streams-guide-java-8/
..TODO..