Why does a java set the type-argument to object when a lambda expression is passed that has the type as parameter- and return-type?

Viewed 118

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

1 Answers
Related