Changes

Java-mis

8,563 bytes added, 14:47, 11 August 2022
no edit summary
[[ Java Enum ]]<br>
[[ Java Lamnda ]]
 
=Java λ=
 
...
</source>
Ez a '''Consumer<? super T>''' generikus interfész implementációt várja paraméterként, ahol a T a List-ben lévő lista elemek típusa. Valójában a A T listaelemek bármilyen leszármazottját elfogadja. A Láthatjuk az implementációból hogy a forEach metódus az összes listaelemen végigmegy, és minden egyes listaelemre meghívja a Consumer<T> implementáció '''accept()''' metódusát. Az A lista elemeket a saját osztály példányának a példány változójából szedi (vagyis ha van egy ArrayList típusú objektumunk, akkor az maga tárolja a lista elemeket egy belső változójában). A Consumer interfész implementáción múlik, hogy mit fog csinálni az adott listaelemmel.  A Consumer implementáció (Consumer<? super T> action) csak egy cső, amibe felülről be tudunk dobni egy T elemet és kidob alul egy R elemet (jelen esetben nincs R elem, mert void típusú a Funkcionális függvény). Tehát az implementációnak nincs állapota. Kap egy inputot és generál belőle egy outputot.  
<br><br>
}}
<br><br>
Nézzünk egy lehetséges implementációt:
<source lang="java">
public class MyConstumer implements Consumer<String>{
@Override
public void accept(String t) {
System.out.println(t);
}
}
</source>
A MyConsumer osztály implementálja a Consumer interfész egyetlen metódusát, az accept-et, egy String-et vár inputba és nem ad vissza semmit.
<br><br>
Ennek az implementációs osztálynak ez lenne egy lehetséges felhasználása:
<source lang="java">
public static void main(String[] args) {
List<String> items = new ArrayList<>();
items.add("A");
items.add("B");
items.add("C");
items.add("D");
items.add("E");
 
Consumer<String> myConsumer = new MyConstumer();
items.forEach(myConsumer);
}
</source>
 
<br><br>
Azonban Java8-tól kezdve bevezették a λ kifejezéseket, mivel név nélküli inline függvény implementációkat készíthetünk, ezek tulajdonképpen input és output paraméterrel rendelkező kód blokkok az alábbi szintaxissal (alább három kül. példa)
<pre>
parameter -> expression
(parameter1, parameter2) -> expression
(parameter1, parameter2) -> { code block }
</pre>
{{warning|Ezek a paraméterek nem a "külső", funkcionális interfészt implementáló java osztályt meghívó metódus bemeneti paraméterei, hanem belső változok. A '''forEach''' esetén pl ezek a lista elemek}}
<br><br>
Az előző példa Lambda alakja a következő lenne:
<source lang="java">
Consumer<String> myConsumer = item -> System.out.println(item);
items.forEach(myConsumer);
</source>
Az alábbi Lambda kifejezéssel inline implementációt készítettünk És a funkcionális interfészhez, ami egy Lista elem típusú paramétert vár és void lambda kifejezést írhatjuk kapásból a visszatérési értéke. forEach argumentumába hogy rövidítsünk:
<source lang="java">
items.forEach(item->System.out.println(item));
</source>
{{note|Az item kívülről nézve nem értelmezhető, szemben egy "normál" függvény hívással, ahol kintről adunk át neki paramétereket. Tehát ez NEM egy külső paraméter. Értelmet csak a forEach belsejében nyer. A lambad kifejezés csak egy állapot nélküli transzformációs függvény, ami inputnak megkapja az '''item'' nevű változót és csinál vele valamit:}}
 
<br>
<br>
Ez egy statikus metódus referencia, amivel azt mondjuk meg, hogy a Consumer implementációja a System.out.print(T) metódust fogja meghívni. A lényeg, hogy egy argumentumos legyen a megadott metódus és fel tudjon dolgozni T lista típusú elemet.
<br>
<br>
 
==Példa 2: saját implementáció==
 
Az alábbi osztálynak van egy darab osztály változója (variable1) és van egy darab metódusa: processVariable. Ez a metódus egy funkcionális interfészt vár input paraméterként.
<source lang="java">
package hu.otp.auth.loginHelper.util;
import java.util.function.Function;
 
public class MyLambadClass {
 
private String variable1;
 
public MyLambadClass(String a) {
this.variable1 = a;
}
 
public void processVariable(Function<String, Integer> fn) {
 
Integer length = fn.apply(variable1);
System.out.println(length.toString());
}
}
</source>
<br>
A processVariable belsejében meghívja a funkcionális interfész '''apply()''' metódusát. (Definiálhattunk volna magunknak saját funkcionális interfész típust, mi most itt egy gyári típust használunk).
 
<source lang="java">
package java.util.function;
 
import java.util.Objects;
 
@FunctionalInterface
public interface Function<T, R> {
R apply(T var1); <<<<<<<<<<<-------------------------------------------------ez itt a lényeg!!!
 
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (v) -> {
return this.apply(before.apply(v));
};
}
 
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (t) -> {
return after.apply(this.apply(t));
};
}
 
static <T> Function<T, T> identity() {
return (t) -> {
return t;
};
}
}
</source>
Láthatjuk, hogy egy implementálandó metódusa van a "Function" interfésznek, ami T típust vár és R típus ad vissza. Tehát ez egy generikus interfész, és bármi is legyen az implementáció, ott majd meg kell mondani, hogy a pl T=String és R=Intger. De az hogy a Function az egy generikus interfész (generic) annak semmi köze ahhoz hogy ő egy Funkcionális interfész.
<br>
<br>
 
===Hagyományos implementáció===
A Function interfésznek elkészíthetjük a saját implementációnkat is:
<source lang="java">
class MyFunctionImpl implements Function<String, Integer> {
@Override
public Integer apply(String s) {
return Integer.valueOf(s.length());
}
}
</source>
Az előbb láthattunk, hogy egy metódust kell kötelezően definiálni a Function interfészen, ez az apply. Az implementáció első sorában kikötöttük, hogy T és R String és Integer lesz. Tehát az apply() implementációnknak S-t kell kapnia paraméterként és Integer-t kell visszaadnia. Ez itt teljesül. Nem csinál mást mint a kapott String hosszát visszaadja.
<br>
<br>
Ahhoz hogy ezt használni tudjuk, példányosítani kell, és át kell adni processVariable() metódusnak, ami egy példányosított funkcionális interfészt implementáló osztályt vár. Vagyis egy olyan objektumot, ami egy olyan osztályból készült, ami implementálja a funkcionális interfész metódusát.
 
 
<source lang="java">
MyLambadClass myLambadClass = new MyLambadClass("adam");
 
MyFunctionImpl myFunction = new MyFunctionImpl();
 
myLambadClass.processVariable(myFunction);
</source>
<br>
A processVariable() implementációját mi készítettük, tudjuk hogy nem csinál mást, mint meghívja a myFunction.apply() metódust.
<br>
===λ implementáció===
Nézzünk az alábbi lehetséges λ implementációt (inline). Az apply-nak átadja a saját osztály változóját, majd az apply által visszaadott Integer-t kiírja a sysout-ra. Az hogy az apply belsejében hogy áll elő az Ingerer és hogy mit csinál a bemenő String paraméterrel teljes egészében az apply implementációra van bízva.
<br>
Nézzünk meg egy lehetséges implementációt:
<source lang="java">
public static void main(String[] args) {
 
MyLambadClass myLambadClass = new MyLambadClass("adam");
myLambadClass.processVariable(var1 -> { return Integer.valueOf(var1.length()); });
 
}
</source>
Ebből az apply lambad kifejezéssel történő inline implementációja az alábbi:
<pre>
var1 -> { return Integer.valueOf(var1.length()); }
</pre>
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 'MyLambadClass' 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 'MyLambadClass', vagy az első példában a ArrayList osztályok.
 
* A 'MyLambadClass' 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
 
<br>
<br>
 
==Példa 3: CompletionStage.thenCompose()==
 
===Mi is ez?===
==thenCompose* '''CompletionStage Interface''': ** ezek egymás után futtatott stage-ek, ezért hívják "Comletion"-nek. Mikor az egyik stage véget ér, potenciálisan elindít egy másik "CompletionStage"-et és így tovább, egymásba láncolva. Minden metódusa egy másik CompletionStage-et ad vissza. De ez csak egy interfész, vmilyen implementációját kell használni. **java.util.concurrent.CompletionStage<T> interface represents a commutation task (either synchronous or asynchronous)==. As all methods declared in this interface return an instance of CompletionStage itself, multiple CompletionStages can be chained together in different ways to complete a group of tasks.* '''CompletableFuture<T> implements CompletionStage<T> and Future<T>''': The static factory methods in this class are the starting points for executing tasks.
===Mi ez? ===Miért működik ez Nézzünk egy példát a lánc hívás: CompletionStage lánchívására. A CompletionStage minden metódusa CompletionStage-el tér vissza, köztük a thenComponse() is, ezért hívható láncban.
<source lang="java">
method1().thenCompose(var1 -> method2(var1))
.whenComplete((result, error) -> { ...}
</source>
  A '''thenComplete(..)''' a '''java.util.concurrent''' csomagban lévő '''CompletionStage''' osztály metódusa. A láncot indító metódusnak értelem szerűen egy '''CompletionStage''' példánnyal kell visszatérnie. <br><br>
A '''thenCompose''' szignatúrája az alábbi:
<source lang="java">
<br>
<br>
Tehát a '''thenCompose(..)''' vár egy '''Function''' interfész implementációt és belül meg fogja hívni az '''apply(..)''' metódusát, aminek át fog adni egy T típusú változót, és R típusú változót fog visszavárni visszatérési értékként. Hogy ezt megértsük, nézzük Nézzük meg a '''thenCompose''' implementációját:
<source lang=java>
public <U> LogCompletableFutureCompletableFuture<U> thenCompose( Function<? super T, ? extends CompletionStage<U>> fn) { return uniComposeStage(null, fn); }  private <V> CompletableFuture<V> uniComposeStage( Executor e, Function<? super T, ? extends CompletionStage<V>> f) { if (f == null) throw new LogCompletableFutureNullPointerException(); CompletableFuture<V> d = newIncompleteFuture(this.stage.thenCompose); Object r, s; Throwable x; if ((tr = result) -== null) unipush(new UniCompose<T,V> (e, d, this, f)); else if (e == null) { if (r instanceof AltResult) { if ((x = ((AltResult)r).ex) != null) { d..result = encodeThrowable(x, r); return d; } r = null; CompletionStage var4;}
try {
var4 @SuppressWarnings("unchecked") T t = (CompletionStageT)fnr; CompletableFuture<V> g = f.apply(t); } catch .toCompletableFuture(Throwable var7) { ... }; ....
}
</source>
Vagyis Anélkül hogy nagyon belemennénk a részletekbe, a lényeg itt is az mint a 2. példában. A CompletableFuture példánynak vannak példány változói. Fogja a T típusú változóját és meghívja vele az '''thenComposeapply(..)''' metódust, és visszavár egy ponton meg fogja hívni CompletionStage leszármazottat. Mivel a CompletionStage hívások láncba fűzhetőek, a hívások egymás után hajtódnak végre. A fenti kódrészlet egy kis varázslással végrehajtja és előveszi a láncban az előző CompletionStage<R> értékét és ebből az R-t adja oda az átadott Function implementáció '''apply(...)''' metódusátmetódusnak, hogy végezze el rajta a szükséges műveleteket. <br><br>Java 8-tól ezt lambda kifejezéssel is le lehet írni inline implementációval, ahol az inline implementáció szimbolizálja jelen esetben az '''Az apply(..)''' vagyis az adott funkcionális interfész implementálandó metódusának metódus inline implementációja tehát mindig egy CompletionStage<R> példánnyal kell hogy visszatérjen, ahonnan az R-t fogja megkapni a következő thenCompose hívás inline implementációjátimplementációja T-ként. Tehát az alábbi kifejezés esetébenPszeudokóddal leírva ez így néz ki:
<source lang="java">
...
.thenCompose(var1 -> method2{ ... //Create a future with string inside new CompletableFuture<String> future = new CompletableFuture<String>(); future.complete("adam"); return future; }) .thenCompose(var1 -> { System.out.println(var1); //will print "adam" ... })
...
</source>
A '''var1''' nem más mint a bemenő paraméter Az első thenCompose(T..) és a '''method2vissza fog térni egy CompletableFuture-el, amiben egy String-et tárol el. A második thenCompose(...)''' visszatérési értéke pedig Rhívás végre fogja hajtani az elsőt, aminek a '''thenCompose''' szignatúrájából következően kötelezően '''CompletionStage''' leszármazottnak kell lennie. <br>A '''var1''' majd annak az eredményéből ki fogja nyerni az "kívülről nézveadam" nem értelmezhető. A '''thenComose''' megkapja az implementációt és a saját változóiból berak egyet a var1 helyére Stringet és azzal futtatja le az '''apply(..)''' implementációját:     {{note|Tehát még egyszer: Az inline implementáció fogja meghívni a funkcionális Funkcionális interfész egyetlen egy implementálandó metódusának belsejét reprezentálja!}}inline implementációt vagyis olyan mint ha ezt csinálná: <brpre>fn.apply("adam");<br/pre>
===Lánc hívás===
Fontos még, hogy java8-tól kezdve a funkcionális interfész hívás láncban az előző inline implementáció visszatérési értékét (R) automatikusan megkapja a következő funkcionális interfész implementáció bemenő paraméterként (T).Tehát az alábbi láncban:
<source lang="java" line start="1">
method1().thenCompose(var1 -> method2(var1))
.thenCompose(this::method3))
.thenCompose(var2 -> { System.out.println(var2.printMe());
System.out.println("That was it"); return new CompletionStage(..) ;
} )
.whenComplete((result, error) -> { ...}
</source>
* A method2() visszatérési értékét (1. sor) kapja meg a method3 input paraméterként.
* A 2. sorban lévő method3 hívás visszatérési érteke bele fog kerülni a var2-be a 3. sorban.
{{note|Tehát az apply(..) inline implementációja csak egy cső, amibe a lambda kifejezések láncban hívásakor mindig a Funkcionális interfész definíciójában szereplő T thenCompose() bedob egy változót és R input és output típusok közlekednek automatikusanvisszavár egy végeredményt. Annak ellenére, hogy minden egyes inline implementáció A változó itt is a láncban egy <pre>? extends CompletionStage<U>></pre> -el kell visszatérjen, ebből saját példány változója (történetesen az U-t fogja megkapni a következő inline implementáció méghozzá ezért: előző futás eredménye)}}
.thenCompose(var2 -> method3(var2))
</source>
<hr>
<br>
<br>
=Java Method Reference and Constructor Reference=
https://www.amitph.com/java-method-and-constructor-reference/
==Method Reference==
 
=Mi kell még=Constructor Reference==
* CompletableFuture: =Stream=https://www.baeldungstackify.com/streams-guide-java-completablefuture (vajon mindig új szálat indít, vagy csak akkor amikor az hatékonyabb) 8/* Exception types and errors * String ..TODO.. list* Lambda + FunctionalInterface + streams és (::)* var változó * Típus, ami csak arra kell, hogy megmondja, hogy null e az érték * volatile * ThreadLocal + MDV* Logging json (correlation id használata) * Reaktív programozás * Reactor (spring)* Spring boot