Difference between revisions of "Java-mis"
(→Mi kell még) |
(→List-forEach()) |
||
Line 62: | Line 62: | ||
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. | 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ért működik ez a lánc hívás: | ||
+ | <source lang="java"> | ||
+ | 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 **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> | ||
+ | |||
+ | A **thenCompose** szignatúrája az alábbi: | ||
+ | <source lang="java"> | ||
+ | <U> LogCompletionStage<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> var1); | ||
+ | </source> | ||
+ | <br> | ||
+ | <br> | ||
+ | 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. | ||
+ | |||
+ | <source lang="java"> | ||
+ | @FunctionalInterface | ||
+ | public interface Function<T, R> { | ||
+ | R apply(T var1); | ||
+ | ... | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | |||
+ | <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. Hogy ezt megértsük, nézzük meg a **thenCompose** implementációját: | ||
+ | <source lang=java> | ||
+ | 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) { | ||
+ | ... | ||
+ | } | ||
+ | .... | ||
+ | } | ||
+ | </source> | ||
+ | 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: | ||
+ | <source lang="java"> | ||
+ | ... | ||
+ | .thenCompose(var1 -> method2(var1)) | ||
+ | ... | ||
+ | </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> | ||
+ | |||
+ | {{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!}} | ||
+ | |||
+ | <br> | ||
+ | <br> | ||
+ | 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 a lambda kifejezések láncban hívásakor mindig a Funkcionális interfész definíciójában szereplő T és R input és output típusok közlekednek automatikusan. }} | ||
+ | |||
+ | |||
+ | <br> | ||
+ | <br> | ||
+ | Na de mit jelent ez a sor: | ||
+ | <source lang="java"> | ||
+ | .thenCompose(this::method3) | ||
+ | </source> | ||
+ | |||
+ | 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. | ||
+ | <pre> | ||
+ | <Melyik osztály>::<melyik metódusa> | ||
+ | </pre> | ||
+ | Ezt akkor lehet alkalmazni, ha amúgy is csak csak metódus hívást írtunk volna a body-ba: | ||
+ | <source lang="java"> | ||
+ | .thenCompose(var2 -> method3(var2)) | ||
+ | </source> | ||
=Mi kell még= | =Mi kell még= |
Revision as of 15:15, 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é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.
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!
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
Tehát a lambda kifejezések láncban hívásakor mindig a Funkcionális interfész definíciójában szereplő T és R input és output típusok közlekednek automatikusan.
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