2014-10-31 17 views
6

Vorrei raggruppare BiFunctions, come nel metodo chainWanted nell'esempio di codice riportato di seguito.Come concatenare BiFunctions?

BiFunction accetta Function come parametro AndThen. è possibile in qualche modo concatenare BiFunctions?

Il codice qui non viene compilato a causa di ciò e non è possibile eseguire BiFunction to Function.

import java.util.function.BiFunction; 
import java.util.function.Function; 

import org.openqa.selenium.remote.RemoteWebDriver; 

public class Wf { 

    BiFunction<RemoteWebDriver, WfParams, RemoteWebDriver> init = this::init; 
    BiFunction<RemoteWebDriver, WfParams, RemoteWebDriver> wait = this::wait; 

    BiFunction<RemoteWebDriver, WfParams, RemoteWebDriver> chainNow = init 
      .andThen(d -> { 
       System.out.println("--------------"); 
       return null; 
      }); 

    BiFunction<RemoteWebDriver, WfParams, RemoteWebDriver> chainWanted = init 
      .andThen((BiFunction) wait); 

    public RemoteWebDriver init(RemoteWebDriver d, WfParams params) { 
     System.out.println("init(d, params)"); 
     return d; 
    } 

    public RemoteWebDriver wait(RemoteWebDriver d, WfParams params) { 
     System.out.println("Wf.wait(d, params)"); 
     return d; 
    } 

    public static void main(String[] args) throws Exception { 
     new Wf().start(); 
    } 

    private void start() { 
     chainNow.apply(null, null); 
    } 
} 
+0

Non vedo come sia possibile concatenare le bifunzioni; il risultato di una bifunzione è un singolo argomento, come intendi incatenarlo in un altro? – fge

risposta

0

Dove dovrebbero le WfParams venire da per l'invocazione di wait? Se si intende riutilizzare lo stesso WfParams per tutte le chiamate di funzioni, è sufficiente inserire WfParams come variabile membro della classe invece di passarlo a ciascuna funzione.

class Wf { 
    private final WfParams params; 

    public Wf(WfParams params) { 
     this.params = params; 
    } 

    UnaryOperator<RemoteWebDriver> init = this::init; 
    UnaryOperator<RemoteWebDriver> wait = this::wait; 

    Function<RemoteWebDriver,RemoteWebDriver> chain = init.andThen(wait); 

    RemoteWebDriver init(RemoteWebDriver d) { 
     // can use WfParams here 
     return d; 
    } 

    RemoteWebDriver wait(RemoteWebDriver d) { 
     // can use WfParams here    
     return d; 
    } 

    private void start() { 
     chain.apply(null); 
    } 

    public static void main(String[] args) { 
     new Wf(new WfParams()).start(); 
    } 
} 

C'è un motivo particolare per cui si desidera utilizzare il concatenamento di funzioni in questo modo? Perché non chiamare semplicemente init(...); wait(...); da start()?

3

Il concatenamento di uno Function a un altro funziona in modo naturale poiché il valore di ritorno della prima funzione viene passato come argomento della funzione successiva e il valore di ritorno di tale funzione viene passato come argomento della funzione successiva e così via. Questo non funziona naturalmente con BiFunction perché accettano due argomenti. Il primo argomento sarebbe il valore restituito dalla funzione precedente, ma quale sarebbe il secondo argomento? Spiega anche il motivo per cui BiFunction consente il concatenamento con andThen a Function anziché un altro BiFunction.

Ciò suggerisce, tuttavia, che sarebbe possibile concatenare uno BiFunction a un altro se ci fosse un modo per fornire il valore per il secondo argomento. Questo può essere fatto creando una funzione di supporto che memorizza il valore per il secondo argomento in una variabile locale. Quindi, un BiFunction può essere convertito in un Function acquisendo quella variabile locale dall'ambiente e utilizzandola come secondo argomento.

Ecco come sarebbe.

BiFunction<RemoteWebDriver, WfParams, RemoteWebDriver> chainWanted = this::chainHelper; 

RemoteWebDriver chainHelper(RemoteWebDriver driver, WfParams params) { 
    return 
     init.andThen(rwd -> wait.apply(rwd, params)) 
      .apply(driver, params); 
} 

// ... 

chainWanted.apply(driver, params); 

Il metodo chainHelper detiene l'argomento params per la cattura in seguito. Chiamiamo init.andThen() per fare il concatenamento. Ma questo richiede un Function mentre wait è un BiFunction. Invece di utilizzare un metodo di riferimento this::wait usiamo l'espressione lambda

rwd -> wait.apply(rwd, params) 

che cattura params dall'ambiente lessicale. Questo dà un'espressione lambda che accetta un singolo argomento e restituisce un singolo valore, quindi ora è un Function che racchiude lo wait che è un BiFunction. Questo è un esempio di applicazione parziale o curring. Infine, chiamiamo il risultato BiFunction usando apply(), passando gli argomenti originali.