'Java 8: Where is TriFunction (and kin) in java.util.function? Or what is the alternative?

I see java.util.function.BiFunction, so I can do this:

BiFunction<Integer, Integer, Integer> f = (x, y) -> { return 0; };

What if that is not good enough and I need TriFunction? It doesn't exist!

TriFunction<Integer, Integer, Integer, Integer> f = (x, y, z) -> { return 0; };

I guess I should add that I know I can define my own TriFunction, I'm just trying to understand the rationale behind not including it in the standard library.



Solution 1:[1]

If you need TriFunction, just do this:

@FunctionalInterface
interface TriFunction<A,B,C,R> {

    R apply(A a, B b, C c);

    default <V> TriFunction<A, B, C, V> andThen(
                                Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (A a, B b, C c) -> after.apply(apply(a, b, c));
    }
}

Following small program shows how it can be used. Remember that result type is specified as a last generic type parameter.

  public class Main {

    public static void main(String[] args) {
        BiFunction<Integer, Long, String> bi = (x,y) -> ""+x+","+y;
        TriFunction<Boolean, Integer, Long, String> tri = (x,y,z) -> ""+x+","+y+","+z;


        System.out.println(bi.apply(1, 2L)); //1,2
        System.out.println(tri.apply(false, 1, 2L)); //false,1,2

        tri = tri.andThen(s -> "["+s+"]");
        System.out.println(tri.apply(true,2,3L)); //[true,2,3]
    }
  }

I guess if there was practical use for TriFunction in java.util.* or java.lang.* it would have been defined. I would never go beyond 22 arguments, though ;-) What I mean by that, all new code that allows to stream collections never required TriFunction as any of the method parameters. So it was not included.

UPDATE

For completeness and following the destructive functions explanation in another answer (related to currying), here is how TriFunction can be emulated without additional interface:

Function<Integer, Function<Integer, UnaryOperator<Integer>>> tri1 = a -> b -> c -> a + b + c;
System.out.println(tri1.apply(1).apply(2).apply(3)); //prints 6

Of course, it is possible to combine functions in other ways, e.g.:

BiFunction<Integer, Integer, UnaryOperator<Integer>> tri2 = (a, b) -> c -> a + b + c;
System.out.println(tri2.apply(1, 2).apply(3)); //prints 6
//partial function can be, of course, extracted this way
UnaryOperator partial = tri2.apply(1,2); //this is partial, eq to c -> 1 + 2 + c;
System.out.println(partial.apply(4)); //prints 7
System.out.println(partial.apply(5)); //prints 8

While currying would be natural to any language that supports functional programming beyond lambdas, Java is not built this way and, while achievable, the code is hard to maintain, and sometimes read. However, it is very helpful as an exercise, and sometimes partial functions have a rightful place in your code.

Solution 2:[2]

Alternative is, add the below dependency,

<dependency>
    <groupId>io.vavr</groupId>
    <artifactId>vavr</artifactId>
    <version>0.9.0</version>
</dependency>

Now, you can use Vavr Function, like below upto 8 arguments,

3 arguments:

Function3<Integer, Integer, Integer, Integer> f = 
      (a, b, c) -> a + b + c;

5 arguments:

Function5<Integer, Integer, Integer, Integer, Integer, Integer> f = 
      (a, b, c, d, e) -> a + b + c + d + e;

Solution 3:[3]

I Have almost the same question and a partial answer. Not sure whether the constructive/deconstructive answer is what the language designers had in mind. I think having 3 and more upto N has valid use cases.

I come from .NET. and in .NET you have Func and Action for void functions. Predicate and some other special cases also exist. See: https://msdn.microsoft.com/en-us/library/bb534960(v=vs.110).aspx

I wonder what the reason was why the language designers opted for Function, Bifunction and did not continue until DecaExiFunction?

The answer to the second part is type erasure. After compilation there is no difference between Func and Func. The following therefore does not compile:

package eu.hanskruse.trackhacks.joepie;

public class Functions{

    @FunctionalInterface
    public interface Func<T1,T2,T3,R>{
        public R apply(T1 t1,T2 t2,T3 t3);
    }

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

Inner functions were used to circumvent another minor problem. Eclipse insisted on having both classes in files named Function in the same directory... Not sure whether this a compiler issue nowadays. But I cannot turn the error of in Eclipse.

Func was used to prevent name clashes with the java Function type.

So if you want to add Func from 3 upto 16 argument you can do two things.

  • Make TriFunc, TesseraFunc,PendeFunc, ...DecaExiFunc etc
    • (Should I use Greek or Latin?)
  • Use package names or classes to make the names different.

Example for the second way:

 package eu.hanskruse.trackhacks.joepie.functions.tri;

        @FunctionalInterface
        public interface Func<T1,T2,T3,R>{
            public R apply(T1 t1,T2 t2,T3 t3);
        }

and

package eu.trackhacks.joepie.functions.tessera;

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

What would be the best approach?

In the above examples I did not include implementations for the andThen() and compose() methods. If you add these you must add 16 overloads each: the TriFunc should have an andthen() with 16 arguments. That would give you a compile error because of circular dependencies. Also you would not have these overloads for Function and BiFunction. Therefore you should also define Func with one argument and Func with two arguments. In .NET circular dependencies would be circumvented by using extension methods which are not present in Java.

Solution 4:[4]

You could also create your own function taking the 3 parameters

@FunctionalInterface
public interface MiddleInterface<F,T,V>{
    boolean isBetween(F from, T to, V middleValue);
}

MiddleInterface<Integer, Integer, Integer> middleInterface = 
(x,y,z) -> x>=y && y<=z; // true

Solution 5:[5]

I found the source code for BiFunction here:

https://github.com/JetBrains/jdk8u_jdk/blob/master/src/share/classes/java/util/function/BiFunction.java

I modified it to create TriFunction. Like BiFunction, it uses andThen() and not compose(), so for some applications that require compose(), it may not be appropriate. It should be fine for normal kinds of objects. A good article on andThen() and compose() can be found here:

http://www.deadcoderising.com/2015-09-07-java-8-functional-composition-using-compose-and-andthen/

import java.util.Objects;
import java.util.function.Function;

/**
 * Represents a function that accepts two arguments and produces a result.
 * This is the three-arity specialization of {@link Function}.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #apply(Object, Object)}.
 *
 * @param <S> the type of the first argument to the function
 * @param <T> the type of the second argument to the function
 * @param <U> the type of the third argument to the function
 * @param <R> the type of the result of the function
 *
 * @see Function
 * @since 1.8
 */
@FunctionalInterface
public interface TriFunction<S, T, U, R> {

    /**
     * Applies this function to the given arguments.
     *
     * @param s the first function argument
     * @param t the second function argument
     * @param u the third function argument
     * @return the function result
     */
    R apply(S s, T t, U u);

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     */
    default <V> TriFunction<S, T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (S s, T t, U u) -> after.apply(apply(s, t, u));
    }
}

Solution 6:[6]

You can't always stop at TriFunction. Sometimes, you may need to pass n number of parameters to your functions. Then support team will have to create an QuadFunction to fix your code. Long term solution would be to create an Object with the extra parameters and then use the ready-made Function or BiFunction.

Solution 7:[7]

Simple Function<T, R> can be used in a nested form to emulate a TriFunction

Below is a simple example -

       final Function<Integer, Function<Integer, Function<Integer, Double>>> function = num1 -> {
            System.out.println("Taking first parameter");
            return num2 -> {
                System.out.println("Taking second parameter");
                return num3 -> {
                    System.out.println("Taking third parameter");
                    return (double)(num1 + num2 + num3);
                };
            };
        };

        final Double result = function.apply(2).apply(3).apply(4);

        System.out.println("Result -> " + result);

OUTPUT -

Taking first parameter
Taking second parameter
Taking third parameter
Result -> 9.0

This logic can be extended to make a function take any desired number of parameters.

Solution 8:[8]

The selected answer is the most helpful, although I find the explanation a bit convoluted.

To simplify, let's say you want a function that adds two strings

The method

String add(String s, String t) {
    return s + t;
}

Would have a function like this with the same behavior:

Function<String,Function<String,String>> add = s -> t -> s + t;

And to call it:

var result = add.apply("hello").apply(" world");

Whether or not this is idiomatic Java that's a different topic.

Solution 9:[9]

There are ready to use Consumer3..Consumer8, Function3..Function8, Predicate3..Predicate8 in reactor.function package of Reactor Addons library that is bundled with Spring Framework.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Amol Damodar
Solution 3
Solution 4 Leandro Maro
Solution 5 Vance
Solution 6 Koushik Roy
Solution 7 Payel Senapati
Solution 8 OscarRyz
Solution 9 Vadzim