Difference between revisions of "Java-mis"

From berki WIKI
Jump to: navigation, search
(Mi ez?)
Line 1: Line 1:
 
+
=Java λ=
  
 
==List-forEach()==
 
==List-forEach()==
Line 32: Line 32:
 
...
 
...
 
</source>
 
</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 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.  
+
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. A T listaelemek bármilyen leszármazottját elfogadja. 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. 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>
  
Line 48: Line 51:
 
}}
 
}}
 
<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>
 +
<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 a funkcionális interfészhez, ami egy Lista elem típusú paramétert vár és void a visszatérési értéke.
+
És a lambda kifejezést írhatjuk kapásból a forEach argumentumába hogy rövidítsünk:
 
<source lang="java">
 
<source lang="java">
 
items.forEach(item->System.out.println(item));
 
items.forEach(item->System.out.println(item));
 
</source>
 
</source>
 +
{{note|Az item kívülről nézve nem értelmezhető. É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>
 
<br>
 
<br>
Line 62: Line 107:
 
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.
  
 +
<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>
 +
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). 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ó.
 +
<br>
 +
<br>
 +
==Példa 3: CompletionStage.thenCompose()==
  
==thenCompose()==
+
===Mi is ez?===
  
===Mi ez? ===
+
* '''CompletionStage Interface''': 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.
Miért működik ez a lánc hívás:
+
* '''CompletableFuture<T> implements CompletionStage<T> and Future<T>''': The static factory methods in this class are the starting points for executing tasks.
 +
 
 +
 
 +
Nézzünk egy példát a 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">
 
<source lang="java">
 
method1().thenCompose(var1 -> method2(var1))
 
method1().thenCompose(var1 -> method2(var1))
Line 75: Line 167:
 
         .whenComplete((result, error) -> { ...}
 
         .whenComplete((result, error) -> { ...}
 
</source>
 
</source>
 
+
A láncot indító metódusnak értelem szerűen egy '''CompletionStage''' példánnyal kell visszatérnie. <br>
 
+
<br>
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:  
 
A '''thenCompose''' szignatúrája az alábbi:  
 
<source lang="java">
 
<source lang="java">
Line 98: Line 188:
 
<br>
 
<br>
 
<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:  
+
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. Nézzük meg a '''thenCompose''' implementációját:  
 
<source lang=java>
 
<source lang=java>
  public <U> LogCompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {
+
    public <U> CompletableFuture<U> thenCompose(
         return new LogCompletableFuture(this.stage.thenCompose((t) -> {
+
        Function<? super T, ? extends CompletionStage<U>> fn) {
             ...
+
         return uniComposeStage(null, fn);
             CompletionStage var4;
+
    }
 +
 
 +
    private <V> CompletableFuture<V> uniComposeStage(
 +
        Executor e, Function<? super T, ? extends CompletionStage<V>> f) {
 +
        if (f == null) throw new NullPointerException();
 +
        CompletableFuture<V> d = newIncompleteFuture();
 +
        Object r, s; Throwable x;
 +
        if ((r = 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;
 +
             }
 
             try {
 
             try {
                 var4 = (CompletionStage)fn.apply(t);
+
                 @SuppressWarnings("unchecked") T t = (T) r;
            } catch (Throwable var7) {
+
                CompletableFuture<V> g = f.apply(t).toCompletableFuture();
              ...
+
        ....
            }
 
          ....
 
 
     }
 
     }
 
</source>  
 
</source>  
Vagyis a '''thenCompose(..)''' egy ponton meg fogja hívni az átadott Function implementáció '''apply()''' metódusát.  
+
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 '''apply(..)''' metódust, és visszavár egy 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 apply(...) metó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 '''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:
+
Az apply(..) 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ója T-ként. Pszeudokóddal leírva ez így néz ki:  
 
<source lang="java">
 
<source lang="java">
 
   ...  
 
   ...  
   .thenCompose(var1 -> method2(var1))
+
   .thenCompose(var1 -> {  ...
 +
                      //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>
 
</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>
+
Az első thenCompose(..) vissza fog térni egy CompletableFuture-el, amiben egy String-et tárol el. A második thenCompose(..) hívás végre fogja hajtani az elsőt, majd annak az eredményéből ki fogja nyerni az "adam" Stringet és azzal fogja meghívni a Funkcionális interfész inline implementációt vagyis olyan mint ha ezt csinálná:
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:
+
<pre>
 
+
fn.apply("adam");
 
+
</pre>
 
 
 
 
{{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>
 
  
===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 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. Annak ellenére, hogy minden egyes inline implementáció a láncban egy <pre>? extends CompletionStage<U>></pre> -el kell visszatérjen, ebből az U-t fogja megkapni a következő inline implementáció méghozzá ezért: }}
+
{{note|Tehát az apply(..) inline implementációja csak egy cső, amibe a thenCompose() bedob egy változót és visszavár egy végeredményt. A változó itt is a saját példány változója (történetesen az előző futás eredménye)}}
  
  
Line 165: Line 260:
 
         .thenCompose(var2 -> method3(var2))
 
         .thenCompose(var2 -> method3(var2))
 
</source>
 
</source>
 
=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
 

Revision as of 17:54, 8 June 2021

Java λ

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. A T listaelemek bármilyen leszármazottját elfogadja. 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. 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.



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ó)



Nézzünk egy lehetséges implementációt:

public class MyConstumer implements Consumer<String>{
	@Override
	public void accept(String t) {
		System.out.println(t);		
	}
}

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.

Ennek az implementációs osztálynak ez lenne egy lehetséges felhasználása:

	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);		
	}



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)

parameter -> expression
(parameter1, parameter2) -> expression
(parameter1, parameter2) -> { code block }



Az előző példa Lambda alakja a következő lenne:

                Consumer<String> myConsumer = item -> System.out.println(item);
		items.forEach(myConsumer);


És a lambda kifejezést írhatjuk kapásból a forEach argumentumába hogy rövidítsünk:

items.forEach(item->System.out.println(item));

{{note|Az item kívülről nézve nem értelmezhető. É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:



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.



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.

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());
	}
}

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). 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.
Nézzünk meg egy lehetséges implementációt:

	public static void main(String[] args) {

		MyLambadClass myLambadClass = new MyLambadClass("adam");		
		myLambadClass.processVariable(var1 -> { return Integer.valueOf(var1.length()); });

	}

Ebből az apply lambad kifejezéssel történő inline implementációja az alábbi:

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ó.

Példa 3: CompletionStage.thenCompose()

Mi is ez?

  • CompletionStage Interface: 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.


Nézzünk egy példát a 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.

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 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, 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. Nézzük meg a thenCompose implementációját:

    public <U> CompletableFuture<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 NullPointerException();
        CompletableFuture<V> d = newIncompleteFuture();
        Object r, s; Throwable x;
        if ((r = 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;
            }
            try {
                @SuppressWarnings("unchecked") T t = (T) r;
                CompletableFuture<V> g = f.apply(t).toCompletableFuture();
        ....
    }

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 apply(..) metódust, és visszavár egy 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 apply(...) metódusnak, hogy végezze el rajta a szükséges műveleteket.

Az apply(..) 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ója T-ként. Pszeudokóddal leírva ez így néz ki:

   ... 
   .thenCompose(var1 -> {  ... 
                      //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"
                      ...
                      })
   ...

Az első thenCompose(..) vissza fog térni egy CompletableFuture-el, amiben egy String-et tárol el. A második thenCompose(..) hívás végre fogja hajtani az elsőt, majd annak az eredményéből ki fogja nyerni az "adam" Stringet és azzal fogja meghívni a Funkcionális interfész inline implementációt vagyis olyan mint ha ezt csinálná:

fn.apply("adam");


ImportantIcon.png

Note
Tehát az apply(..) inline implementációja csak egy cső, amibe a thenCompose() bedob egy változót és visszavár egy végeredményt. A változó itt is a saját példány változója (történetesen az előző futás eredménye)




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))