Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't the compiler deduce the type of an inline lambda parameter to Stream.of?

Why does Stream.of() require a cast to take an inline lambda expression or method reference?

Consider this:

 Function<String,String> f = String::toUpperCase; // works without cast
 Stream.of(f); // works without cast
 //Stream.of(String::toUpperCase); // Error - target type must be functional interface
 Stream.of((Function<String,String>) String::toUpperCase); //OK

The assignment to the variable f does not require a cast, but when used as an inline parameter to Stream.of a cast is required. Why?

like image 751
Gonen I Avatar asked Dec 23 '22 15:12

Gonen I


1 Answers

A lambda or method reference by itself doesn't have a type, it derives its type from the context (e.g. the variable it's assigned to), or in other words it's contextually typed. When you use Stream.of(...) without further context to infer the type (e.g. returning, so the type is specified by the return type, or assigning to a variable or parameter, or using explicit generic parameters), there is no type information available to construct the lambda or method reference.

The reason is that Java doesn't know whether you want a Function<String, String>, or a UnaryOperator<String>, or some other functional interface with a compatible signature.

You need to do something like:

public Stream<Function<String, String>> example1() {
    return Stream.of(String::toUpperCase);
}

public void example2() {
    Stream<Function<String, String>> stream = Stream.of(String::toUpperCase);
}

public void example3() {
    Stream.<Function<String, String>>of(String::toUpperCase);
}

public void example4() {
    doSomething(Stream.of(String::toUpperCase));
}

private void doSomething(Stream<Function<String, String>> stream) {
    // ...
}

See also the Java Tutorial on lambdas, specifically section Target Typing:

[...] The data type that these methods expect is called the target type. To determine the type of a lambda expression, the Java compiler uses the target type of the context or situation in which the lambda expression was found. It follows that you can only use lambda expressions in situations in which the Java compiler can determine a target type:

  • Variable declarations
  • Assignments
  • Return statements
  • Array initializers
  • Method or constructor arguments
  • Lambda expression bodies
  • Conditional expressions, ?:
  • Cast expressions
like image 115
Mark Rotteveel Avatar answered Dec 28 '22 23:12

Mark Rotteveel