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

14

176

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.

Disgruntle answered 23/8, 2013 at 10:11 Comment(5)
with bifunction interface, you can easly define N-function class, if you define trifunction as separate interface, first sb will be asking why not quadofunction, and second, you need to duplicate all methods which takes Bifunction as parameterChute
There is a point of diminishing returns for APIs like this. (Personally, I think JDK8 passed it a while back, but this is beyond even that.)Skirr
I believe the rationale was to say that Function and BiFunction were fully implemented with objects and native types. Including TriFunctions with all the various variations would blow up the JRE with classes and methods.Aboveboard
Short answer. In Java, if you don't see it, you build your own (see Alex P answers of course). Sidenote, in C#, the implementers of dotnet gave you pre-canned ones (up to 16 arguments), but without the prefix names ("Bi" here) : see learn.microsoft.com/en-us/dotnet/api/… Just a simple "Func". So this is one of the places I prefer dotnet over java. Please don't turn this comment section into a h0ly war. and limit comments to BiFunction only.Folsom
Just like any thing else. If the API doesn't provide it, then code it yourself.Supersensitive
O
94

As far as I know, there are only two kinds of functions, destructive and constructive.

While constructive function, as the name implies, constructs something, a destructive one destroys something, but not in the way you may think now.

For example, the function

Function<Integer,Integer> f = (x,y) -> x + y  

is a constructive one. As you need to construct something. In the example you constructed the tuple (x,y). Constructive functions have the problem, of being not able to handle infinite arguments. But the worst thing is, you can't just leave an argument open. You can't just say "well, let x := 1" and try out every y you may like to try. You have to construct every time the whole tuple with x := 1. So if you like to see what the functions return for y := 1, y := 2, y := 3 you have to write f(1,1) , f(1,2) , f(1,3).

In Java 8, constructive functions should be handled (most of the time) by using method references because there's not much advantage of using a constructive lambda function. They are a bit like static methods. You can use them, but they have no real state.

The other type is the destructive one, it takes something and dismantles it as far as needed. For example, the destructive function

Function<Integer, Function<Integer, Integer>> g = x -> (y -> x + y) 

does the same as the function f which was constructive. The benefits of a destructive function are, you can handle now infinite arguments, which is especially convenient for streams, and you can just leave arguments open. So if you again want to see what would the result be like if x := 1 and y := 1 , y := 2 , y := 3 , you can say h = g(1) and h(1) is the result for y := 1, h(2) for y := 2 and h(3) for y := 3.

So here you have a fixed state! That's quite dynamic and that's most of the time that what we want from a lambda.

Patterns like Factory are a lot easier if you can just put in a function which does the work for you.

Destructive ones are easily combined with each other. If the type is right you can just compose them as you like. Using that, you can easily define morphisms which make (with immutable values) testing a lot easier!

You can do that too with a constructive one, but destructive composition looks nicer and more like a list or a decorator, and the constructive one looks a lot like a tree. And things like backtracking with constructive functions are just not nice. You can just save the partial functions of a destructive one (dynamic programming), and on "backtrack" just use the old destructive function. That makes code a lot smaller and better readable. With constructive functions you have more or less to remember all arguments, which can be a lot.

So why is there a need for BiFunction should be more of question than why there is no TriFunction?

First of all, a lot of time you just have a few values (less than 3) and need just a result, so a normal destructive function would not be needed at all, a constructive one would do fine. And there are things like monads which really needs a constructive function. But aside from that, there are not really a lot of good reasons why there is a BiFunction at all. Which doesn't mean it should be removed! I fight for my Monads until I die!

So if you have a lot of arguments, which you can't combine into a logical container class, and if you need the function to be constructive, use a method reference. Otherwise try to use the new gained ability of destructive functions, you may find yourself doing a lot of things with a lot less code lines.

Older answered 15/2, 2014 at 13:7 Comment(15)
You answered my question...I think...I don't know if the java language designers are coming from this line of thinking, but I'm not well-versed in functional programming. Thank you for the explanation.Disgruntle
I updated my answer too. The problem with the currying approach is that Java was never meant to do that. Unless you switch to Scala, using g becomes a hassle: g.apply(1).apply(2); etc.Sollars
I've never seen the terms constructive and destructive being used to refer to the concepts you describe. I think curried and non-curried are more common terms.Veron
The first function example is not syntactically correct. It should be BiFunction and not Function, because it takes two input arguments.Fugitive
IMO BiFunction was created to allow easy data reduction, and most Streams terminal operations are just data reductions. A good example is BinaryOperator<T>, used in many Collectors. A first element is reduced with the second one, than can then be reduced with the next one, and so on. Of course, you could create a Function<T, Function<T, T> func = x -> (y -> /*reduction code here*/). But, seriously? All of this when you can simply do BinaryOperator<T> func = (x, y) -> /*reduction code here*/. Plus, this data reduction approach seems a lot like your "destructive" approach to me.Arbitrage
I had no idea about this way of create functions!, awesome man!!!. You make my day!Lycanthropy
How did this get so many upvotes? It's a terrible and confusing answer, because it's based on the premise that Function<Integer,Integer> f = (x,y) -> x + y is valid Java, which it isn't. That should be a BiFunction to begin with!Gaudette
There is a common use case for BiFunction or BiConsumer: whevever you wand to pass a method as argument, as in public class Foo { public void bar(String arg) {...} public void doSomething(BiConsumer<Foo, String> method, String arg) { method.accept(this, arg); } public void callBar(String arg) { doSomething(Foo::bar, arg); } } - and hence the need for TriConsumer etc...Aubert
SmallTalk lang libs have these functions up to 16 args for a while, afaik. The way of thinking described in this answer should be considered harmfull. The only way for this to live is in pure academic vacuum. In case of java, nesting function definition is inproductive and will cause cascaded design problems and excessive object creation. Do not follow this advice IRLVickivickie
The first example should be BiFunction<Integer,Integer,Integer> I think it's unnecessarily complicated that Java uses those statically typed function definitions. There's no reason to have multiple function/consumer definitions, the arguments should be variableWitkowski
The language that caters for this kind of thinking would be functional languages like Haskell. They do a lot of function sum (a, ...more_a): return a + sum(more_a) (non-working pseudocode) shenanigans.Sebrinasebum
What is "infinite arguments" supposed to mean here and in what way would it be convenient when using streams?Emmert
There’s also callbacks, and I like mine with arbitrary many parameters.Saxophone
I keep coming to this question once in a year or so, every time wondering "Where does Java have the TriFunction again?". Every time, I bump into this answer. It makes no sense whatsoever. It's so annoying. ಠ_ಠLittle
Quite a big assumption here. I can think of use case of TriFunction in financial domain. E.g: to calculate a butterfly price product where you'll need to calculate all 3 elements with different factor between first element, second one and third one and apply different mathematical function to those 3 elements.Rare
S
218

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.

Sollars answered 29/10, 2013 at 4:7 Comment(5)
Thanks for the solution. And yes, there is definitely use for BiFunction, TriFunction, ... Otherwise people would not search for it. It guess the whole lambda thing is just too new for Oracle right now and will be extended in later Java versions. At the moment it's more a proof of concept.Astrakhan
Hy @Alex can you please define following line. whats happening here 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)); }Isherwood
@MuneebNasir - it allows you to do function composition: TriFunction<Integer,Integer,Integer,Integer> comp = (x,y,z) -> x + y + z; comp = comp.andThen(s -> s * 2); int result = comp.apply(1, 2, 3); //12 See #19835111Sollars
Added andThen() usage example to the answer.Sollars
Not only currying is not well adapted to Java language, but also, correct me if I'm wrong, but BiFunction is used in the Stream API to perform data reduction, which looks a lot like the currying approach to me: you never take more than two arguments, and you can process any number of elements, one reduction at a time (see my comment on the accepted answer, I'd be happy to know if I'm wrong to see it that way).Arbitrage
O
94

As far as I know, there are only two kinds of functions, destructive and constructive.

While constructive function, as the name implies, constructs something, a destructive one destroys something, but not in the way you may think now.

For example, the function

Function<Integer,Integer> f = (x,y) -> x + y  

is a constructive one. As you need to construct something. In the example you constructed the tuple (x,y). Constructive functions have the problem, of being not able to handle infinite arguments. But the worst thing is, you can't just leave an argument open. You can't just say "well, let x := 1" and try out every y you may like to try. You have to construct every time the whole tuple with x := 1. So if you like to see what the functions return for y := 1, y := 2, y := 3 you have to write f(1,1) , f(1,2) , f(1,3).

In Java 8, constructive functions should be handled (most of the time) by using method references because there's not much advantage of using a constructive lambda function. They are a bit like static methods. You can use them, but they have no real state.

The other type is the destructive one, it takes something and dismantles it as far as needed. For example, the destructive function

Function<Integer, Function<Integer, Integer>> g = x -> (y -> x + y) 

does the same as the function f which was constructive. The benefits of a destructive function are, you can handle now infinite arguments, which is especially convenient for streams, and you can just leave arguments open. So if you again want to see what would the result be like if x := 1 and y := 1 , y := 2 , y := 3 , you can say h = g(1) and h(1) is the result for y := 1, h(2) for y := 2 and h(3) for y := 3.

So here you have a fixed state! That's quite dynamic and that's most of the time that what we want from a lambda.

Patterns like Factory are a lot easier if you can just put in a function which does the work for you.

Destructive ones are easily combined with each other. If the type is right you can just compose them as you like. Using that, you can easily define morphisms which make (with immutable values) testing a lot easier!

You can do that too with a constructive one, but destructive composition looks nicer and more like a list or a decorator, and the constructive one looks a lot like a tree. And things like backtracking with constructive functions are just not nice. You can just save the partial functions of a destructive one (dynamic programming), and on "backtrack" just use the old destructive function. That makes code a lot smaller and better readable. With constructive functions you have more or less to remember all arguments, which can be a lot.

So why is there a need for BiFunction should be more of question than why there is no TriFunction?

First of all, a lot of time you just have a few values (less than 3) and need just a result, so a normal destructive function would not be needed at all, a constructive one would do fine. And there are things like monads which really needs a constructive function. But aside from that, there are not really a lot of good reasons why there is a BiFunction at all. Which doesn't mean it should be removed! I fight for my Monads until I die!

So if you have a lot of arguments, which you can't combine into a logical container class, and if you need the function to be constructive, use a method reference. Otherwise try to use the new gained ability of destructive functions, you may find yourself doing a lot of things with a lot less code lines.

Older answered 15/2, 2014 at 13:7 Comment(15)
You answered my question...I think...I don't know if the java language designers are coming from this line of thinking, but I'm not well-versed in functional programming. Thank you for the explanation.Disgruntle
I updated my answer too. The problem with the currying approach is that Java was never meant to do that. Unless you switch to Scala, using g becomes a hassle: g.apply(1).apply(2); etc.Sollars
I've never seen the terms constructive and destructive being used to refer to the concepts you describe. I think curried and non-curried are more common terms.Veron
The first function example is not syntactically correct. It should be BiFunction and not Function, because it takes two input arguments.Fugitive
IMO BiFunction was created to allow easy data reduction, and most Streams terminal operations are just data reductions. A good example is BinaryOperator<T>, used in many Collectors. A first element is reduced with the second one, than can then be reduced with the next one, and so on. Of course, you could create a Function<T, Function<T, T> func = x -> (y -> /*reduction code here*/). But, seriously? All of this when you can simply do BinaryOperator<T> func = (x, y) -> /*reduction code here*/. Plus, this data reduction approach seems a lot like your "destructive" approach to me.Arbitrage
I had no idea about this way of create functions!, awesome man!!!. You make my day!Lycanthropy
How did this get so many upvotes? It's a terrible and confusing answer, because it's based on the premise that Function<Integer,Integer> f = (x,y) -> x + y is valid Java, which it isn't. That should be a BiFunction to begin with!Gaudette
There is a common use case for BiFunction or BiConsumer: whevever you wand to pass a method as argument, as in public class Foo { public void bar(String arg) {...} public void doSomething(BiConsumer<Foo, String> method, String arg) { method.accept(this, arg); } public void callBar(String arg) { doSomething(Foo::bar, arg); } } - and hence the need for TriConsumer etc...Aubert
SmallTalk lang libs have these functions up to 16 args for a while, afaik. The way of thinking described in this answer should be considered harmfull. The only way for this to live is in pure academic vacuum. In case of java, nesting function definition is inproductive and will cause cascaded design problems and excessive object creation. Do not follow this advice IRLVickivickie
The first example should be BiFunction<Integer,Integer,Integer> I think it's unnecessarily complicated that Java uses those statically typed function definitions. There's no reason to have multiple function/consumer definitions, the arguments should be variableWitkowski
The language that caters for this kind of thinking would be functional languages like Haskell. They do a lot of function sum (a, ...more_a): return a + sum(more_a) (non-working pseudocode) shenanigans.Sebrinasebum
What is "infinite arguments" supposed to mean here and in what way would it be convenient when using streams?Emmert
There’s also callbacks, and I like mine with arbitrary many parameters.Saxophone
I keep coming to this question once in a year or so, every time wondering "Where does Java have the TriFunction again?". Every time, I bump into this answer. It makes no sense whatsoever. It's so annoying. ಠ_ಠLittle
Quite a big assumption here. I can think of use case of TriFunction in financial domain. E.g: to calculate a butterfly price product where you'll need to calculate all 3 elements with different factor between first element, second one and third one and apply different mathematical function to those 3 elements.Rare
F
21

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;
Fiberboard answered 21/1, 2019 at 7:23 Comment(2)
I was about to update my answer to mention vavr, but you were first, so I upvoted. If you get to the point you need a TriFunction, there is a big chance you will be better off by using vavr library - it makes functional style programming as bearable in Java as possible.Sollars
The answer is good. The idea that is isn't already in the framework : GAAAAAAA.Folsom
F
8

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.

Forbearance answered 17/10, 2016 at 17:28 Comment(1)
Why would you need andThen with 16 arguments? The result of a function in Java is a single value. andThen takes this value and does something with it. Also, there is no issue with naming. Class names should be different and be in different files named the same - following the logic set by Java language developers with Function and BiFunction. Also, all these different names are needed if argument types are different. One can create a VargFunction(T, R) { R apply(T.. t) ... } for single type.Sollars
T
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
Troika answered 19/5, 2019 at 11:26 Comment(0)
I
3

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));
    }
}
Ine answered 28/11, 2018 at 20:17 Comment(0)
V
3

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.

Villarreal answered 15/2, 2021 at 17:48 Comment(3)
This should be the accepted answer. Google "currying".Node
Mater of fact I ended up writing currying code a full 6 months before I came to know curryingVillarreal
Kinda like, "To know recursion, you must first know recursion"?Node
H
1

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.

Hooks answered 14/2, 2020 at 8:19 Comment(0)
W
1

Use commons lang3 with

org.apache.commons.lang3.function.TriFunction
Wot answered 8/1, 2023 at 16:26 Comment(0)
K
1

You could use TriFunction from commons-lang

(example from https://www.baeldung.com/java-trifunction)

TriFunction<Integer, Integer, Integer, Integer> multiplyThenAdd = (x, y, z) -> x * y + z;
Kolivas answered 5/7, 2023 at 11:7 Comment(0)
E
0

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.

Enoch answered 3/3, 2021 at 0:18 Comment(0)
A
0

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.

Apostles answered 29/7, 2021 at 7:40 Comment(0)
N
0

I think the most straightforward way to handle this is to simply make a class which defines all of the arguments you want to handle, and use a simple Function or Consumer. Is there a real reason you would want TriFunction instead of for example:

public class MyDataObject {
  public String arg0;
  public int arg1;
  public Date argWhatever;
}

//...

Function<Integer, MyDataObject> fn = (data) -> {
  // do stuff with data.arg0, data.arg1, etc
  return 0; // or whatever 
};

Imho this is the cleaner way to do it anyway if its getting more complicated than two arguments.

Norite answered 17/10, 2022 at 19:4 Comment(0)
P
0

You can accomplish a solution with currying. However it tends to be messy in Java: Function<Function<Function<Function<...

Penitence answered 26/4, 2023 at 15:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.