Reputation: 567
I would like to chain BiFunctions, like in the method chainWanted
in the code sample below.
BiFunction takes Function as a parameter of AndThen. is it possible to somehow chain BiFunctions ?
The code here doesn't compile because of this and I cannot cast 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);
}
}
Upvotes: 11
Views: 3948
Reputation: 141
Event though it doesn't seems like a usual chaining but I came to the following approach, conceptually similar with the one proposed by Stuart Marks :
BiFunction<Param1, Param2, Param1> firstBiFunc = (p1, p2) -> {
// DO SOME STUFF
return secondBiFunc.apply(p1, p2);
};
BiFunction<Param1, Param2, Param1> secondBiFunc = (p1, p2) -> {
// DO SOME STUFF
return p1
};
Upvotes: 0
Reputation: 2604
I did something like this - created my custom BiFunction. The idea being:
Return type is same as the second argument
First argument is passed internally to chained biFunction
public interface BiFunctionCustom<T, U> extends BiFunction<T,U,U> {
default BiFunctionCustom<T, U> andThen(BiFunctionCustom<T, U> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(t, apply(t, u));
}
}
Upvotes: 0
Reputation: 132600
Chaining of one Function
to another works naturally because the return value of the first function is passed as the argument to the next function, and that function's return value is passed as the argument to the subsequent function, and so forth. This doesn't work naturally with BiFunction
because they take two arguments. The first argument would be the return value from the previous function, but what would the second argument be? It also explains why BiFunction
allows chaining with andThen
to a Function
instead of to another BiFunction
.
This suggests, however, that it would be possible to chain one BiFunction
to another if there were some way of providing the value for second argument. This can be done by creating a helper function that stores the value for the second argument in a local variable. Then, a BiFunction
can be converted into a Function
by capturing that local variable from the environment and using it as the second argument.
Here's what that would look like.
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);
The chainHelper
method holds the params
argument for later capture. We call init.andThen()
in order to do the chaining. But this requires a Function
whereas wait
is a BiFunction
. Instead of using a method reference this::wait
we use the lambda expression
rwd -> wait.apply(rwd, params)
which captures params
from the lexical environment. This gives a lambda expression that takes a single argument and returns a single value, so it's now a Function
that wraps the wait
which is a BiFunction
. This is an example of partial application or currying. Finally, we call the resulting BiFunction
using apply()
, passing the original arguments.
Upvotes: 6
Reputation: 28183
Where should the WfParams come from for the invocation of wait
? If you mean to reuse the same WfParams for all the functions calls, just put WfParams as a class member variable instead of passing it to each function.
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();
}
}
Is there a particular reason you want to use function chaining like that? Why not simply call init(...); wait(...);
from start()
?
Upvotes: 0