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 unnknow wich 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

Community
  • 1
  • 1
SilentStorm
  • 149
  • 1
  • 2
  • 10

1 Answers1

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);
}
*/
HopefullyHelpful
  • 1,652
  • 3
  • 21
  • 37
  • 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 Mar 25 '16 at 15:15
  • @SilentStorm Are you talking about case 3 here? – Paul Boddington Mar 25 '16 at 15:29
  • @PaulBoddington Yes indeed. – SilentStorm Mar 25 '16 at 15:30
  • @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`, so the method `returnValue` requires a `String`. However you are passing `test((a) -> " ")`, which is a `Test`, 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.test((a) -> " ").returnValue("");`. I think the compilation issue here is by design, not a bug. I'll try to find explanation on SO. – Paul Boddington Mar 25 '16 at 15:34
  • @PaulBoddington I updated the question to contain a better example. There you can see, that the problem only happens when chaining. – SilentStorm Mar 25 '16 at 15:57