Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java lambda returning a lambda

Tags:

I am trying to do what seems to be a relatively basic thing in the new JDK 8 land of functional programming, but I can't get it to work. I have this working code:

import java.util.*; import java.util.concurrent.*; import java.util.stream.*;  public class so1 {    public static void main() {       List<Number> l = new ArrayList<>(Arrays.asList(1, 2, 3));       List<Callable<Object>> checks = l.stream().                map(n -> (Callable<Object>) () -> {                   System.out.println(n);                   return null;                }).                collect(Collectors.toList());    } } 

It takes a list of numbers and produces a list of functions that can print them out. However, the explicit cast to Callable seems redundant. It seems to me and to IntelliJ. And we both agree that this should also work:

List<Callable<Object>> checks = l.stream().        map(n -> () -> {           System.out.println(n);           return null;        }).        collect(Collectors.toList()); 

However I get an error:

so1.java:10: error: incompatible types: cannot infer type-variable(s) R       List<Callable<Object>> checks = l.stream().map(n -> () -> {System.out.println(n); return null;}).collect(Collectors.toList());                                                     ^     (argument mismatch; bad return type in lambda expression       Object is not a functional interface)   where R,T are type-variables:     R extends Object declared in method <R>map(Function<? super T,? extends R>)     T extends Object declared in interface Stream 1 error 
like image 883
MK. Avatar asked Nov 11 '14 19:11

MK.


People also ask

Can you return a lambda function in Java?

Java For TestersA return statement is not an expression in a lambda expression. We must enclose statements in braces ({}). However, we do not have to enclose a void method invocation in braces. The return type of a method in which lambda expression used in a return statement must be a functional interface.

How do you return a value from lambda?

Lambda functions are syntactically restricted to return a single expression. You can use them as an anonymous function inside other functions. The lambda functions do not need a return statement, they always return a single expression.

What is the return type of lambda in Java?

What is the return type of lambda expression? Explanation: Lambda expression enables us to pass functionality as an argument to another method, such as what action should be taken when someone clicks a button. 4.

Can lambda return string?

A free variable can be a constant or a variable defined in the enclosing scope of the function. The lambda function assigned to full_name takes two arguments and returns a string interpolating the two parameters first and last .

How to use a return statement in lambda expression in Java?

How to use a return statement in lambda expression in Java? A return statement is not an expression in a lambda expression. We must enclose statements in braces ( {}). However, we do not have to enclose a void method invocation in braces.

What is a lambda expression?

A lambda expression is a short block of code which takes in parameters and returns a value. Lambda expressions are similar to methods, but they do not need a name and they can be implemented right in the body of a method. The simplest lambda expression contains a single parameter and an expression:

What is Lambda body in Java?

In Java, the lambda body is of two types. 1. A body with a single expression This type of lambda body is known as the expression body. 2. A body that consists of a block of code.

What is argument list in lambda expression in Java?

Java Lambda Expression Syntax 1 Argument-list: It can be empty or non-empty as well. 2 Arrow-token: It is used to link arguments-list and body of expression. 3 Body: It contains expressions and statements for lambda expression.


2 Answers

You hit a limitation of Java 8’s target typing which applies to the receiver of a method invocation. While target typing works (most of the times) for parameter types it does not work for the object or expression on which you invoke the method.

Here, l.stream(). map(n -> () -> { System.out.println(n); return null; }) is the receiver of the collect(Collectors.toList()) method invocation, so the target type List<Callable<Object>> is not considered for it.

It’s easy to prove that nested lambda expressions work if the target type is know, e.g.

static <T> Function<T,Callable<Object>> toCallable() {     return n -> () -> {         System.out.println(n);          return null;     }; } 

works without problems and you can use it to solve your original problem as

List<Callable<Object>> checks = l.stream()     .map(toCallable()).collect(Collectors.toList()); 

You can also solve the problem by introducing a helper method which changes the role of the first expression from method receiver to a parameter

// turns the Stream s from receiver to a parameter static <T, R, A> R collect(Stream<T> s, Collector<? super T, A, R> collector) {     return s.collect(collector); } 

and rewrite the original expression as

List<Callable<Object>> checks = collect(l.stream().map(     n -> () -> {         System.out.println(n);          return null;     }), Collectors.toList()); 

This does not reduce the complexity of the code but can be compiled without any problems. For me, it’s a déjà vu. When Java 5 and Generics came out, programmers had to repeat the type parameters on new expressions while simply wrapping the expression into a generic method proved that inferring the type is no problem. It took until Java 7 before programmers were allowed to omit these unnecessary repetition of the type arguments (using the “diamond operator”). Now we have a similar situation, wrapping an invocation expression into another method, turning the receiver into a parameter, proves that this limitation is unnecessary. So maybe we get rid of this limitation in Java 10…

like image 127
Holger Avatar answered Sep 17 '22 13:09

Holger


I ran into this same issue and was able to solve it by explicitly specifying the generic type-parameter to map like so:

List<Callable<Object>> checks = l.stream().    <Callable<Object>>map(n -> () -> {       System.out.println(n);        return null;    }).    collect(Collectors.toList()); 
like image 30
Vivin Paliath Avatar answered Sep 20 '22 13:09

Vivin Paliath