Implementing a Consumer that takes 5 (or any other arbitrary number) of arguments – Functional style programming – extending API
200. Implementing a Consumer that takes 5 (or any other arbitrary number) of arguments
Before continuing with this problem I strongly recommend you to read Problem 199.Writing a custom Consumer that takes 5 arguments can be done as follows:
@FunctionalInterface
public interface FiveConsumer <T1, T2, T3, T4, T5> {
void accept (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5);
}
This is the five-arity specialization of the Java Consumer exactly as the built-in BiConsumer is the two-arity specialization of Consumer.We can use FiveConsumer in conjunction with the PL4 formula as follows (here, we compute y for x = 40.3):
FiveConsumer<Double, Double, Double, Double, Double>
pl4c = (a, b, c, d, x) -> Logistics.pl4(a, b, c, d, x);
pl4c.accept(4.19, -1.10, 12.65, 0.03, 40.3);
The Logistics.pl4() is the method that contains the formula and displays the result:
public static void pl4(Double a, Double b,
Double c, Double d, Double x) {
System.out.println(d + ((a – d) / (1
+ (Math.pow(x / c, b)))));
}
Next, let’s see how we can partially apply a Function.
201. Partially applying a Function
A Function that is partially applied is a Function that applies only a part of its arguments and returns another Function. For instance, here is a TriFunction (a functional function with three arguments) that contains the apply() method next to two default methods that partially apply this function:
@FunctionalInterface
public interface TriFunction <T1, T2, T3, R> {
R apply(T1 t1, T2 t2, T3 t3);
default BiFunction<T2, T3, R> applyOnly(T1 t1) {
return (t2, t3) -> apply(t1, t2, t3);
}
default Function<T3, R> applyOnly(T1 t1, T2 t2) {
return (t3) -> apply(t1, t2, t3);
}
}
As you can see, applyOnly(T1 t1), applies only the t1 argument and returns a BiFunction. On the other hand, applyOnly(T1 t1, T2 t2), applies only t1 and t2, and returns a Function.Let’s see how we can use these methods. For instance, let’s consider the formula (a+b+c)2 = a2+b2+c2+2ab+2bc+2ca, which can be shaped via the TriFunction as follows:
TriFunction<Double, Double, Double, Double> abc2 = (a, b, c)
-> Math.pow(a, 2) + Math.pow(b, 2) + Math.pow(c, 2)
+ 2.0*a*b + 2*b*c + 2*c*a;
System.out.println(“abc2 (1): ” + abc2.apply(1.0, 2.0, 1.0));
System.out.println(“abc2 (2): ” + abc2.apply(1.0, 2.0, 2.0));
System.out.println(“abc2 (3): ” + abc2.apply(1.0, 2.0, 3.0));
Here, we call apply(T1 t1, T2 t2, T3 t3) three times. As you can see, only the c term has a different value per call, while a and b are constantly equal with 1.0, respectively 2.0. This means that we can use apply(T1 t1, T2 t2) for a and b, and apply(T1 t1) for c as follows:
Function<Double, Double> abc2Only1 = abc2.applyOnly(1.0, 2.0);
System.out.println(“abc2Only1 (1): ” + abc2Only1.apply(1.0));
System.out.println(“abc2Only1 (2): ” + abc2Only1.apply(2.0));
System.out.println(“abc2Only1 (3): ” + abc2Only1.apply(3.0));
If we assume that only a is constant (1.0) while b and c have different values per call then we can use apply(T1 t1) for a, and apply(T1 t1, T2 t2) for b and c as follows:
BiFunction<Double, Double, Double> abc2Only2
= abc2.applyOnly(1.0);
System.out.println(“abc2Only2 (1): “
+ abc2Only2.apply(2.0, 3.0));
System.out.println(“abc2Only2 (2): “
+ abc2Only2.apply(1.0, 2.0));
System.out.println(“abc2Only2 (3): “
+ abc2Only2.apply(3.0, 2.0));
Mission accomplished!
Summary
This chapter covered 24 problems. Most of them were focused on working with predicates, functions, and collectors, but we also covered JDK 16 mapMulti(), refactoring imperative code to functional, and much more.
Leave a Reply