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?
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.
Using Lambda ExpressionsThe lambda expression should have the same number of parameters and the same return type as that method.
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.
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.
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).
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)));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With