6

I want to define a simple lambda, add up three integers:

(int a, int b,int c)->a+b+c

For this simple case, I have two options:

  1. Define a functional interface. The single abstract method should be int add(int a, int b,int c), the method name and varable names don't matter.

  2. Use method reference. I have to define a class/method or use an existing method that has the signature int add(int a, int b,int c)

In both cases, for the very simple lambda, I have to get back to the OOP world(interface, class, etc)

But in scala, it is very simple to define a function in place: val add= (a:Int,b:Int,c:Int)=>a+b+c

Tom
  • 5,848
  • 12
  • 44
  • 104
  • 1
    Possible duplicate of [Can a java lambda have more than 1 parameter?](https://stackoverflow.com/questions/27872387/can-a-java-lambda-have-more-than-1-parameter) – Shaido Oct 26 '17 at 03:38
  • 1
    Actually, it is the very same thing in Scala. There needs to be a corresponding interface for the lambda. The difference is just that the Scala standard library already contains an number of such interfaces, namely the `Function0` … `Function22` traits. The Java standard library *also* contains a number of pre-made interfaces, there's just a lot less of them. (And, because of `void` and primitives, there need to be a lot more. E.g., there is *one* interface for functions with 2 parameters in Scala, but in Java you need 810 interfaces for the same thing.) – Jörg W Mittag Oct 26 '17 at 09:26
  • Scala has two awesome things which are very friendly to end users. 1. User can directly use X=>Y as the function type 2. The primitive types, int,..void are abstract over, so that there is not much redundant things to the end user. – Tom Oct 26 '17 at 09:31

4 Answers4

6

In Java, you must target every lambda expression to a specific functional interface type. This is by design, and in practice it means that functions are not first-class citizens of the language.

So, yes, you have to get back to OOP world.

You can, however, use the Function built-in type to define your function in its currified form:

Function<Integer, Function<Integer, Function<Integer, Integer>>> add =
    a -> b -> c -> a + b + c;

Usage:

int result = add.apply(1).apply(2).apply(3); // 6

Though I doubt this is readable at all...

fps
  • 33,623
  • 8
  • 55
  • 110
  • 1
    Thanks. This is the key point to my question: functions are not first-class citizens of the language, – Tom Oct 26 '17 at 05:55
  • 2
    Or even shorter, and without boxing overhead: `IntFunction add = a -> (b, c) -> a + b + c;` and `int result = add.apply(1).applyAsInt(2, 3);`… – Holger Oct 26 '17 at 07:38
6

Actually, defining a function in Scala works exactly the same as option 1, except 1) the functional interface scala.Function3[A1, A2, A3, B] is contained in the standard Scala library; 2) the compiler fakes supporting Int in generics (which ends up being converted to boxed java.lang.Integer.

You can't really do anything about 2) in Java, but 1) is solved trivially by using a library which defines Function3 and so on (or writing your own). There are http://www.functionaljava.org/, http://www.vavr.io/, https://mvnrepository.com/artifact/uk.org.potes.java8/java8-currying (which only has those interfaces and nothing more). Probably a few others.

Scala wins on type inference, of course, in two ways:

  1. FunctionN is the "default" functional interface for lambda expressions, so it doesn't need to be specified.

  2. If you specify arguments, return type can normally be inferred.

But these considerations matter more when assigning lambdas to a field or a variable, and less when passing them to a method, which is probably more common.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • But Function3, the 3 input arguments are of type T1,T2,T3, the return value is R. This definition has an embarrassing situation that the return value can't be void, `Function3 print = (Integer a, Integer b, Integer c)->System.out.println("" + a+b+c)` doesn't compile. For this case, I have to define Consumer3 – Tom Oct 26 '17 at 07:29
  • Yes, those too. – Alexey Romanov Oct 26 '17 at 07:33
  • 2
    The key point is, a method expecting a certain function as argument has to specify a parameter type describing the expected function shape. That’s what the functional interface is for. On the other hand, the caller specifying the function as lambda expression doesn’t need to specify a type. – Holger Oct 26 '17 at 07:44
  • In Java though you could create a function interface that uses int's specifically avoiding boxing while Function3 requires boxing primitives. Could the boxing be avoided in Scala as well by using a specialized SAM interface instead of the regular Function3? – puhlen Oct 26 '17 at 13:56
  • 1
    @puhlen Yes, I believe it works the same way (since Scala 2.12). I haven't checked the generated bytecode myself, though. – Alexey Romanov Oct 26 '17 at 14:30
2

In your specific use case where the type of operation and operand is same both the time(a+b+c => a + (b+c)), you can make use of a BiFunction defined as:-

BiFunction<Integer, Integer, Integer> f = (x, y) -> x + y;
System.out.println(f.apply(a, f.apply(b, c))); // a + b + c

Or another alternate as suggested by @Igor would be to make use of would be to sum over an IntStream.

Naman
  • 27,789
  • 26
  • 218
  • 353
  • 1
    @Tom I guess your comparison in terms of readability is that of a built-in method to do so and a self-defined one. In which case (1) in your question, defining an FI over the same and accepting a definite number of args is what I would prefer to have a certain amount of flexibility OR (2) for a use case in which there are a variable number of args. – Naman Oct 26 '17 at 03:54
  • 1
    @Tom Moreover the way I would look at it is defining a function. If a modular function does some operation on applying it once, twice, thrice... N times, I might not come up with its implementation of doing it N number of times unless I find my application benefitting from it. So, when Java can come up with something called a TriFunction, QuadFunction.., the question would be, shall that be needed in terms of complexity Vs convenience. <= All of this IMHO. – Naman Oct 26 '17 at 04:03
  • how about this post? https://stackoverflow.com/questions/30125296/how-to-sum-a-list-of-integers-with-java-streams – Igor Oct 26 '17 at 04:17
1

You’re looking for the following:

int sum = IntStream.of(a, b, c).sum();
Naman
  • 27,789
  • 26
  • 218
  • 353
Igor
  • 159
  • 5