3

When a lambda expression is passed to a method it is possible to retrieve its return-type and the parameter-types (if specifically given by the caller) using type-parameters.

What I don't understand is that java seems to discard the type information given by the return-type of a lambda expression if the expression also uses the return-type as a parameter-type.

It is really hard to explain this question with words. Therefore I wrote the sample code below for further clarification.

//Case 1 @FunctionalInterface interface Test<R> { void returnValue(R takes); } static <R> R test(Test<R> test) { //... Do something with test } public static void main(final String[] args) { test((a) -> System.out.println("called")); //This call will always return an Object //This is clear. It is totally unknown which type a has at compile-time } //-------------------------------------------------------------------------- //Case 2 @FunctionalInterface interface Test<R> { R returnValue(); } static <R> R test(Test<R> test) { //... Do something with test } public static void main(final String[] args) { test(() -> " "); //This call will always return a String //This is clear. R is specified to be a String by the return value. } //-------------------------------------------------------------------------- //Case 3 @FunctionalInterface interface Test<R> { R returnValue(R takes); } static <R> R test(Test<R> test) { //... Do something with test } public static void main(final String[] args) { test((a) -> " "); //This call will always return an Object //This it not clear. R is specified to be a String by the return value //Why doesn't it return a String ? } 

Edit: Going deeper into the problem I noticed that the problem only really occurs when chaining calls. The code below demonstrates this. It was compiled in eclipse using java version 1.8.0_73.

package test; public class TestLambdaGenerics { @FunctionalInterface interface Test<R> { R returnValue(R takes); } static <R> Test<R> test(final Test<R> test) { // ... Do something with test return test; } public static void main(final String[] args) { final Test<String> t = test((a) -> " "); // Above works fine final String t2 = test((a) -> " ").returnValue(" "); // Above terminates with output: // Exception in thread "main" java.lang.Error: Unresolved compilation problem: // Type mismatch: cannot convert from Object to String // // at test.TestLambdaGenerics.main(TestLambdaGenerics.java:18) } } 

Edit 2:

Question is resolved "chain-calling" with type-inference is just not supported by java at the moment.

See: this question or this article

4
  • 2
    In case 3, String b = test((a) -> " "); compiles without issue, so the type String is inferred. I don't understand your issue. Commented Mar 25, 2016 at 14:44
  • If you are getting compiler errors, can you tell us the version of java and compiler you are using? Commented Mar 25, 2016 at 14:48
  • I'm using the version 1.8.0_73 of java and I'm compiling with eclipse. See the comment on @HopefullyHelpful s answer. Commented Mar 25, 2016 at 15:20
  • I think it's a duplicate of this: stackoverflow.com/a/24798163/3973077 Commented Mar 25, 2016 at 16:13

1 Answer 1

0

I tested your examples and in case 2/3 the returnValue always has the class String.

/* //Case 1 public class LambdaGenerics { public static void main(final String[] args) { System.out.println(test((a) -> System.out.println("called")).getClass()); //This call will always return a Test<Object> //This is clear. It is totally unknown which type a has at compile-time } static <R> Test<R> test(Test<R> test) { //... Do something with test return test; } } @FunctionalInterface interface Test<R> { void returnValue(R takes); } */ //-------------------------------------------------------------------------- /* //Case 2 public class LambdaGenerics { public static void main(final String[] args) { System.out.println(test(() -> "test").returnValue().getClass()); //This call will always return a Test<Object> //This is clear. It is totally unknown which type a has at compile-time } static <R> Test<R> test(Test<R> test) { //... Do something with test return test; } } @FunctionalInterface interface Test<R> { R returnValue(); } */ //-------------------------------------------------------------------------- /* //Case 3 public class LambdaGenerics { public static void main(final String[] args) { System.out.println(test((a) -> " ").returnValue(test((a) -> " ")).getClass()); //This call will always return a Test<Object> //This is clear. It is totally unknown which type a has at compile-time } static <R> Test<R> test(Test<R> test) { //... Do something with test return test; } } @FunctionalInterface interface Test<R> { R returnValue(R takes); } */ 
Sign up to request clarification or add additional context in comments.

5 Comments

Adding the following to your main-method: final String t = test((a) -> " ").returnValue(test((a) -> " ")); Will yield the excpetion: Exception in thread "main" java.lang.Error: Unresolved compilation problem: Type mismatch: cannot convert from Object to String
@SilentStorm Are you talking about case 3 here?
@PaulBoddington Yes indeed.
@SilentStorm In that case I'm a bit confused about what String t = test((a) -> " ").returnValue(test((a) -> " ")); is supposed to mean. test((a) -> " ") is a Test<String>, so the method returnValue requires a String. However you are passing test((a) -> " "), which is a Test<String>, not a String. If I write String t = Main.test((a) -> " ").returnValue(""); I get compilation problems which I can solve by doing this: String t = Main.<String>test((a) -> " ").returnValue("");. I think the compilation issue here is by design, not a bug. I'll try to find explanation on SO.
@PaulBoddington I updated the question to contain a better example. There you can see, that the problem only happens when chaining.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.