Difference between revisions of "Java-mis"
(→thenCompose()) |
(→Mi ez?) |
||
Line 122: | Line 122: | ||
</source> | </source> | ||
A '''var1''' nem más mint a bemenő paraméter (T) és a '''method2(...)''' visszatérési értéke pedig R, aminek a '''thenCompose''' szignatúrájából következően kötelezően '''CompletionStage''' leszármazottnak kell lennie. <br> | A '''var1''' nem más mint a bemenő paraméter (T) és a '''method2(...)''' visszatérési értéke pedig R, aminek a '''thenCompose''' szignatúrájából következően kötelezően '''CompletionStage''' leszármazottnak kell lennie. <br> | ||
+ | A '''var1''' "kívülről nézve" nem értelmezhető. A '''thenComose''' megkapja az implementációt és a saját változóiból berak egyet a var1 helyére és azzal futtatja le az '''apply(..)''' implementációját: | ||
+ | |||
+ | |||
+ | |||
{{note|Tehát még egyszer: Az inline implementáció a funkcionális interfész egyetlen egy implementálandó metódusának belsejét reprezentálja!}} | {{note|Tehát még egyszer: Az inline implementáció a funkcionális interfész egyetlen egy implementálandó metódusának belsejét reprezentálja!}} | ||
Line 127: | Line 131: | ||
<br> | <br> | ||
<br> | <br> | ||
+ | |||
===Lánc hívás=== | ===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: | 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: |
Revision as of 16:13, 8 June 2021
List-forEach()
List<String> items = new ArrayList<>();
items.add("A");
items.add("B");
items.add("C");
items.add("D");
items.add("E");
//lambda
//Output : A,B,C,D,E
items.forEach(item->System.out.println(item));
Ez miért működik?
A List a java.lang.Iterale osztály leszármazottja. Ebben van egy default metódus implementáció a forEach-re.
package java.lang;
public interface Iterable<T> {
...
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
...
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 T listaelemek bármilyen leszármazottját elfogadja. 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 implementáción múlik, hogy mit fog csinálni az adott listaelemmel.
A Consumer interfészben az accept az egyetlen absztrakt metódus, tehát a Consumer egy funkcionális interfész, ahogy ez az annotációból is látszik:
package java.util.function;
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
Note
A @FunctionalInterface annotáció hatására a fordító ellenőrzi, hogy az adott interfész valóban megfelel e a funkcionális interfészekkel támasztott követelményeknek.
- Csak interfészre kerülhet ilyen annotáció
- Az interfésznek pontosan 1 darab absztrakt metódusa van (vagyis implementálandó)
Az alábbi Lambda kifejezéssel inline implementációt készítettünk a funkcionális interfészhez, ami egy Lista elem típusú paramétert vár és void a visszatérési értéke.
items.forEach(item->System.out.println(item));
A Lambda kifejezést kicserélhetjük method reference-re is:
items.forEach(System.out::print);
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.
thenCompose()
Mi ez?
Miért működik ez a lánc hívás:
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) -> { ...}
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.
A thenCompose szignatúrája az alábbi:
<U> LogCompletionStage<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> var1);
Láthatjuk, hogy egy Function interfészt vár paraméterül. A 'Function' interfésze nem más mint egy "gyári" funkcionális interfész típus (olyan mint az előző példába a 'Consumer' interfész volt). A funkcionális interfészekkel szemben támasztott követelmény, hogy egy nem implementált metódusuk legyen, ami ebben az esetben az apply(). A T az input típusa, míg R a visszatérési típus.
@FunctionalInterface
public interface Function<T, R> {
R apply(T var1);
...
}
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. Hogy ezt megértsük, nézzük meg a thenCompose implementációját:
public <U> LogCompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {
return new LogCompletableFuture(this.stage.thenCompose((t) -> {
...
CompletionStage var4;
try {
var4 = (CompletionStage)fn.apply(t);
} catch (Throwable var7) {
...
}
....
}
Vagyis a thenCompose(..) egy ponton meg fogja hívni az átadott Function implementáció apply() metódusát.
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 apply(..) vagyis az adott funkcionális interfész implementálandó metódusának az inline implementációját. Tehát az alábbi kifejezés esetében:
...
.thenCompose(var1 -> method2(var1))
...
A var1 nem más mint a bemenő paraméter (T) és a method2(...) visszatérési értéke pedig R, aminek a thenCompose szignatúrájából következően kötelezően CompletionStage leszármazottnak kell lennie.
A var1 "kívülről nézve" nem értelmezhető. A thenComose megkapja az implementációt és a saját változóiból berak egyet a var1 helyére és azzal futtatja le az apply(..) implementációját:
Note
Tehát még egyszer: Az inline implementáció a funkcionális interfész egyetlen egy implementálandó metódusának belsejét reprezentálja!
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:
1 method1().thenCompose(var1 -> method2(var1))
2 .thenCompose(this::method3))
3 .thenCompose(var2 -> { System.out.println(var2.printMe());
4 System.out.println("That was it"); return new CompletionStage(..) ;
5 } )
6 .whenComplete((result, error) -> { ...}
- 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
? extends CompletionStage<U>>-el kell visszatérjen, ebből az U-t fogja megkapni a következő inline implementáció méghozzá ezért:
Rövidítés
Na de mit jelent ez a sor:
.thenCompose(this::method3)
Ez csak egy rövidítés. Egy statikus metódus referencia. Ahelyett hogy megadnánk egy inline implementációt, azt mondjuk meg, hogy ez a függvény végzi el azt a feladatot, amit az inline implementációnk végzett volna el.
<Melyik osztály>::<melyik metódusa>
Ezt akkor lehet alkalmazni, ha amúgy is csak csak metódus hívást írtunk volna a body-ba:
.thenCompose(var2 -> method3(var2))
Mi kell még
- CompletableFuture: https://www.baeldung.com/java-completablefuture (vajon mindig új szálat indít, vagy csak akkor amikor az hatékonyabb)
- Exception types and errors
- String ... 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