Implementing a Function that takes 5 (or any other arbitrary number) of arguments – Functional style programming – extending API

199. Implementing a Function that takes 5 (or any other arbitrary number) of arguments

We know that Java already has java.util.function.Function and the specialization of it, java.util.function.BiFunction. The Function interface defines the method apply(T, t), while BiFunction has apply(T t, U u).In this context, we can define a TriFunction, FourFunction, or why not a FiveFunction functional interface as follows (all of these are specializations of Function):

@FunctionalInterface
public interface FiveFunction <T1, T2, T3, T4, T5, R> {
  
  R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5);
}

As its name suggests, this functional interface takes 5 arguments.Now, let’s use it! Let’s assume that we have the following model:

public class PL4 {
  private final double a;
  private final double b;
  private final double c;
  private final double d;
  private final double x;
  public PL4(double a, double b,
             double c, double d, double x) {      
    this.a = a;
    this.b = b;
    this.c = c;
    this.d = d;
    this.x = x;
  }
  // getters
  public double compute() {
    return d + ((a – d) / (1 + (Math.pow(x / c, b))));
  }
  // equals(), hashCode(), toString()
}

The compute() method shapes a formula known as the 4-Parameter Logistic (4PL – https://www.myassays.com/four-parameter-logistic-regression.html). Without getting into irrelevant details, we pass as inputs four variables (a, b, c, and d), and for different values of the x coordinate we compute the y coordinate. The (x, y) pair of coordinates describe a curve (linear graphic).We need a PL4 instance for each x coordinate and for each such instance, we call the compute() method. This means that we can use the FiveFunction interface in Logistics via the following helper:

public final class Logistics {
  …
  public static <T1, T2, T3, T4, X, R> R create(
      T1 t1, T2 t2, T3 t3, T4 t4, X x,
      FiveFunction<T1, T2, T3, T4, X, R> f) {
       
    return f.apply(t1, t2, t3, t4, x);
  }
  …
}

This act as a factory for PL4:

PL4 pl4_1 = Logistics.create(
    4.19, -1.10, 12.65, 0.03, 40.3, PL4::new);
PL4 pl4_2 = Logistics.create(
    4.19, -1.10, 12.65, 0.03, 100.0, PL4::new);

PL4 pl4_8 = Logistics.create(
    4.19, -1.10, 12.65, 0.03, 1400.6, PL4::new);
System.out.println(pl4_1.compute());
System.out.println(pl4_2.compute());

System.out.println(pl4_8.compute());

But, if all we need is just the list of y coordinates then we can write a helper method in Logistics as follows:

public final class Logistics {
  …
  public static <T1, T2, T3, T4, X, R> List<R> compute(
      T1 t1, T2 t2, T3 t3, T4 t4, List<X> allX,
      FiveFunction<T1, T2, T3, T4, X, R> f) {
    List<R> allY = new ArrayList<>();
    for (X x : allX) {
      allY.add(f.apply(t1, t2, t3, t4, x));
    }
    return allY;
  }
  …
}

We can call this method as follows (here, we pass the 4PL formula, but it can be any other formula with 5 double parameters):

FiveFunction<Double, Double, Double, Double, Double, Double>
    pl4 = (a, b, c, d, x) -> d + ((a – d) /
                            (1 + (Math.pow(x / c, b))));      
List<Double> allX = List.of(40.3, 100.0, 250.2, 400.1,
                            600.6, 800.4, 1150.4, 1400.6);      
List<Double> allY = Logistics.compute(4.19, -1.10, 12.65,
                                      0.03, allX, pl4);

You can find the complete example in the bundled code.