Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 - Inferring generic return type with lambdas

I am trying to preserve the Generic type information from the return type of a lambda expression across some higher-order-functions in Java 8. I have simplified my actual code down to this test case. The problem is not what I expect the code to do exactly, but rather than I expect the generic type(s) R to be inferred as java.lang.String and carried through the function calls.

import java.util.function.Function;

public class AdamTest {

    public final static void main(final String args[]) {
        final AdamTest adamTest = new AdamTest();

        final String s = adamTest.thing2(7).apply(i -> i.toString());
        System.out.println(s);
    }


    private <R> R fn1(final Function<Integer, R> op) {
        return op.apply(10);
    }

    private <R> Function<Function<Integer, R>, R> thing2(final Integer val) {
        return fn1(i -> new Function<Function<Integer, R>, R>() {
                @Override
                public R apply(Function<Integer, R> op) {
                    return op.apply(val * i);
                }
            }
        );
    }
}

At the moment this code does not compile because of this line final String s = adamTest.thing2(7).apply(i -> i.toString());. I think I have something subtly wrong with my type bounds, as the compiler does not seem to be able to infer the return type of thing2(7).apply and reports Object rather than String which I was hoping for.

How do I get the generic type bounds correct so that the correct return type i.e. java.lang.String is inferred by the compiler?

like image 761
adamretter Avatar asked Mar 06 '15 11:03

adamretter


People also ask

What is the return type of lambda expression in Java 8?

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.

Can lambda expression have return type?

Using Lambda ExpressionsThe lambda expression should have the same number of parameters and the same return type as that method.

Is Java 8 lambda use parameter type inference?

Type Inference means that the data type of any expression (e.g. method return type or parameter type) can be deduced automatically by the compiler. Groovy language is a good example of programming languages supporting Type Inference. Similarly, Java 8 Lambda expressions also support Type inference.

How target type is inferred for the lambda expression?

The target type of the lambda expressions is inferred from the context, which is an assignment statement to a Callable<String> reference variable. Subsequently, the reference variable is used to invoke the call() method.


2 Answers

As already said, those statements are evaluated from left to right.

To force the compiler to use the correct type, you can simply write it as

final String s = adamTest.<String>thing2(7).apply(String::valueOf);

EDIT: As per the comments, the lambda expression can be substituted by a method reference (looks cleaner).

like image 166
Seelenvirtuose Avatar answered Oct 26 '22 04:10

Seelenvirtuose


I don't think that it can be fixed without casts (maybe someone knows better). An alternative is to split your statements in two lines:

Function<Function<Integer, String>, String> thing2 = adamTest.thing2(7);
final String s = thing2.apply(i -> i.toString());

Also note that thing2 can be simplified:

private <R> Function<Function<Integer, R>, R> thing2(final Integer val) {
  return fn1(i -> (op -> op.apply(val * i)));
}
like image 5
assylias Avatar answered Oct 26 '22 03:10

assylias