Multi-arity functions in Java

Let me remind you: arity is the number of parameters of a function. Accordingly, multi-arity functions are functions with several parameters. In Java 8, functions were introduced with one and two input parameters. But what if there are more parameters?

When you need a lot of input parameters

In Java, there is a Function <X, R> and BiFunction <X, Y, R>, where X and Y are types of input parameters, and R is the type of the output parameter. But functions with three and more input parameters must be determined by yourself. But how?

For example, to further define functions with three input parameters, you must first once determine the interface:

@FunctionalInterface
public interface Function3Arity<A, B, C, R>  {
R apply(A a, B b, C c);
}

After that, you can determine the specific variations of the ternary (three-ary) functions. For example, like this:

private static Function3Arity<Integer, String, Integer, String> 
f3 = (a, op, b) ->{return "" + a + op + b + "=" + (a+b);};

Let’s check how it works:

@Test
public void testFunction3Arity() {
String result = f3.apply(2, "+", 3);
assertEquals("2+3=5", result);
}

When you need a lot of output parameters

We considered an example where there were many input parameters. And what if we have a lot of output parameters?
As you know, Java allows using by return only one primitive element or object. And I would like to be able to distinguish between input and output parameters at the signature level of the function. Those. have type signatures:

(c,d) = f(a,b)

Unfortunately, you can not do it directly. The output parameters must be structured somehow. To do this, you can create a temporary object or write parameters to the list (List <?>). The first way is heavyweight and the second is unpleasant with the loss of static control over the types of output parameters, if these types are different.
From my point of view, the more elegant is the use of <TupleN <X1, X2, ..Xn>.
For example, the Tuple2 class looks like this:

public class Tuple2<A, B> {
public final A a1;
public final B a2;

public Tuple2(A t, B u) {
a1 = Objects.requireNonNull(t);
a2 = Objects.requireNonNull(u);
}

@Override
public boolean equals(Object o) {…}

@Override
public int hashCode() {…}
}

Using this class, a function with three input and two output parameters can be defined like this:

private static Function3Arity<Integer, String, Integer, Tuple2<Integer, String>> f3And2 =
(a, op, b) ->{
int intValue = a + b;
String sValue = "" + a + op + b + "=" + (a+b);
return new Tuple2<>(intValue, sValue);
};

Let’s check how it works:

@Test
public void testFunction3And2Arity() {
Tuple2
assertEquals(5, result.a1);
assertEquals("2+3=5", result.a2);
}

Final Rule

1. If your function has 3 or more input parameters, you need to define a new N-ary interface and use it to further define specific functions.
2. If your function has 2 or more output parameters, define the TupleN class and package the parameters into it before returning from the function with return.

Code examples you will find in my project on GitHub here
Illustration: geralt