Why does Java type inference fail to distinguish between Function and Consumer?

Roger Keays

Given the following identity functions:

<T> Consumer<T> f(Consumer<T> c) { return c; }          // (1)
<T,R> Function<T,R> f(Function<T, R> c) { return c; }   // (2)

I observe the following behaviour in JDK 11 and JDK 17:

void _void() {}
f(x -> {});                   // okay, dispatches to (1)
f(x -> { return; });          // okay, dispatches to (1)
f(x -> { _void(); });         // okay, dispatches to (1)
f(x -> _void());              // should dispatch to (1)
|  Error:
|  reference to f is ambiguous
|    both method f(java.util.function.Function<java.lang.Object,java.lang.Object>) in  
     and method f(java.util.function.Consumer<java.lang.Object>) in  match

int _one() { return 1; }
f(x -> 1);                    // okay, dispatches to (2)
f(x -> { return 1; });        // okay, dispatches to (2)
f(x -> { return _one(); });   // okay, dispatches to (2)
f(x -> _one());               // should dispatch to (2)
|  Error:
|  reference to f is ambiguous
|    both method <T,R>f(java.util.function.Function<T,R>) in
     and method <T>f(java.util.function.Consumer<T>) in  match

Why can't the compiler resolve these symbols by using the return type of the expression? The curly brace versions work fine, and I would have thought they would be the more difficult cases. I understand that you can explicity cast the lambda function, but that defeats the purpose of what I am trying to achieve.

ernest_k

x -> _void() and x -> one() are expected to be compatible with Consumer<T> (with the result of one() to be discarded).

When the lambda body is of a block type, the compiler additionally checks the "return" compatibility. The JLS is rather explicit about void/value compatibility for block bodies:

A block lambda body is void-compatible if every return statement in the block has the form return;. A block lambda body is value-compatible if it cannot complete normally (§14.21) and every return statement in the block has the form return Expression;.

While that doesn't say why the single-expression bodies fail, it says exactly why block bodies compile: the compiler looks at the return forms to judge on those bodies' compatibility with Consumer or Function (in this case).

For the method invocation expressions, the fact that this is allowed:

Consumer<Integer> c = x -> one(); //discarded result
Function<T, Integer> f = x -> one(); //returned result

doesn't enable the compiler to resolve the conflict that you observed. You can rewrite the same lambda expression with block bodies to resolve the conflict, and that's simply because block bodies are checked differently, by spec.


I guess I'm trying to say that the more natural question is "why block bodies compile at all in this case", given that we normally don't expect return types (forms?) to participate in overload resolution. But lambda expressions' congruence with types is something else, isn't it... I think this (that block type helps target type inference) is the special behavior.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Java

Why does type inference fail here?

From Dev

Why does TypeScript type inference fail in this case?

From Dev

Why does this Typescript generic type inference fail?

From Dev

Why does type inference fail for a polymorphic function applied to different inputs withing the same function

From Java

Why does this code fail to compile, citing type inference as the cause?

From Java

Why does type inference fail for lambda, but succeed for equivalent method reference?

From Dev

Why does indexing an explicitly typed vector fail with a type inference error?

From Java

Why does Java not support type inference for constructors?

From Dev

Why Scala fail to recognize Null type as a subtype of T in generic function type parameter inference?

From Java

Why does Java type inference for generic supertypes break here?

From Dev

Why does Java generics type inference break in chained method calls?

From Java

Why does the Java 8 generic type inference pick this overload?

From Java

Why does a Java method reference with return type match the Consumer interface?

From Dev

Why does this branch break type inference?

From Dev

why does guard break type inference?

From Dev

Differences between Red's 5 function types, and why does it distinguish them?

From Dev

Why does this function fail to typecheck?

From Dev

Why this lambda function does not remember type, in Java?

From Dev

Why is the type inference lost when union type is returned from function?

From Dev

Why type inference is not able to infer a type in a function callback?

From Dev

Typescript type inference does not work with function type guards

From Dev

Why does g++ fail init of std::function<> from type with conversion operator and inaccessible function call operators?

From Java

When does Java type inference produce an infinite type?

From Dev

Why does the preprocessor distinguish between number and character tokens?

From Dev

Why and how does R distinguish between two constructions of a file name?

From

Why does type conversion fail at compile time?

From Dev

Flow: Why does `instanceof Type` fail?

From Dev

type inference of (>>)(>>) (function composition)

From Dev

Why does type inference only work with a pipe here?

Related Related

  1. 1

    Why does type inference fail here?

  2. 2

    Why does TypeScript type inference fail in this case?

  3. 3

    Why does this Typescript generic type inference fail?

  4. 4

    Why does type inference fail for a polymorphic function applied to different inputs withing the same function

  5. 5

    Why does this code fail to compile, citing type inference as the cause?

  6. 6

    Why does type inference fail for lambda, but succeed for equivalent method reference?

  7. 7

    Why does indexing an explicitly typed vector fail with a type inference error?

  8. 8

    Why does Java not support type inference for constructors?

  9. 9

    Why Scala fail to recognize Null type as a subtype of T in generic function type parameter inference?

  10. 10

    Why does Java type inference for generic supertypes break here?

  11. 11

    Why does Java generics type inference break in chained method calls?

  12. 12

    Why does the Java 8 generic type inference pick this overload?

  13. 13

    Why does a Java method reference with return type match the Consumer interface?

  14. 14

    Why does this branch break type inference?

  15. 15

    why does guard break type inference?

  16. 16

    Differences between Red's 5 function types, and why does it distinguish them?

  17. 17

    Why does this function fail to typecheck?

  18. 18

    Why this lambda function does not remember type, in Java?

  19. 19

    Why is the type inference lost when union type is returned from function?

  20. 20

    Why type inference is not able to infer a type in a function callback?

  21. 21

    Typescript type inference does not work with function type guards

  22. 22

    Why does g++ fail init of std::function<> from type with conversion operator and inaccessible function call operators?

  23. 23

    When does Java type inference produce an infinite type?

  24. 24

    Why does the preprocessor distinguish between number and character tokens?

  25. 25

    Why and how does R distinguish between two constructions of a file name?

  26. 26

    Why does type conversion fail at compile time?

  27. 27

    Flow: Why does `instanceof Type` fail?

  28. 28

    type inference of (>>)(>>) (function composition)

  29. 29

    Why does type inference only work with a pipe here?

HotTag

Archive