Difference between revisions of "Java-mis"

From berki WIKI
Jump to: navigation, search
(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);
}
ImportantIcon.png

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.

ImportantIcon.png

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.
ImportantIcon.png

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