Explain Codes LogoExplain Codes Logo

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

java
function-engineering
interview-preparation
best-practices
Nikita BarsukovbyNikita Barsukov·Feb 26, 2025
TLDR

For a custom TriFunction in Java 8 to handle three arguments, create an interface as shown below:

@FunctionalInterface public interface TriFunction<A, B, C, R> { R apply(A a, B b, C c); } // Now let's sum three numbers (you can substitute addition with your own logic) TriFunction<Integer, Integer, Integer, Integer> addThree = (x, y, z) -> x + y + z; int sum = addThree.apply(1, 2, 3); // "I've got three troubles and the sum ain't one" - Jay-Z Codes // Outputs 6

Define the TriFunction, declare your method with three parameters, and voila, you 've just designed a functional multi-arity solution ready for your lambdas and streams.

Alternatives to custom TriFunction

Java 8 does not provide TriFunction under its java.util.function package, but don't fret! There are alternative solutions.

The nesting game with functions

The art of nesting Function instances allows you to handle multitudinous arguments. It's like those Russian Matryoshka dolls, only less ominous.

BiFunction<Function<Integer, Integer>, Integer, Integer> addPartial = (function, y) -> function.apply(y); // Functions within functions - Incepteen integer stacks! Function<Integer, Integer> plusTwo = z -> z + 2; int sum = addPartial.apply(plusTwo, 3); // Outputs 5

Currying: Spice up your Java code

Currying is a technique in functional programming where a function with multiple arguments is transformed into a sequence of functions, each with a single argument. It has nothing to do with delicious Indian food, sadly.

// Don't curry in a hurry. Observe: Function<Integer, Function<Integer, Function<Integer, Integer>>> add = x -> y -> z -> x + y + z; int sum = add.apply(1).apply(2).apply(3); // Outputs 6

Vavr library: Your outer-verse Java buddy

The Vavr library brings many functional goodies not available in vanilla Java. Let's add a TriFunction using Vavr, but be mindful of external dependencies.

// Remember, Vavr is not your crazy, old bat auntie. io.vavr.Function3<Integer, Integer, Integer, Integer> addThree = (x, y, z) -> x + y + z; int sum = addThree.apply(1, 2, 3); // Outputs 6

Linking the function train: The art of composition

Function sequence with composed functionality

Extend TriFunction using the andThen method for chain functionality:

TriFunction<Integer, Integer, Integer, Integer> addThree = (a, b, c) -> a + b + c; Function<Integer, String> stringify = (i) -> "Result: " + i; // Let's turn numbers into words Function<Integer, Function<Integer, Function<Integer, String>>> composedFunction = addThree.andThen(stringify); // C-C-C-Combo! String result = composedFunction.apply(1).apply(2).apply(3); // Outputs "Result: 6"

Pro tip: Steer clear of circular dependencies

Avoid circular dependencies by refraining from incorporating andThen() or compose() methods in your custom functional interfaces. Stick to lean, mean interfaces for optimum interoperability.

Prudent practices

Beware of erasure!

When fabricating custom functional interfaces, beware of type erasure. Use unique package names or naming novelties to clear the fog.

The constructive vs. destructive debate

Using a healthy mix of destructive (tearing down) and constructive (building up) functions can enhance your code's flexibility and readability. Remember, all structures are temporary in the city of Java.

Digging deeper

For those who are never content with the surface level:

Dynamic dips

In dynamic programming scenarios, having the ability to save partial functions can be a game-changer. With Java 8 functions, we have the keys to skipping the middleman and looking right under the hood.

Vavr for the win

Using Vavr's set of multi-arity functional interfaces, you could augment your code's efficiency and expressiveness beyond the typical Java landscape.

Lambda Express: Speedy evaluation guaranteed

Fine-tuned lambdas can add both conciseness and clarity to custom functional interfaces, in turn leading to more elegant solutions.