This question already has an answer here:
This question is pretty closely related to another one. However, I feel like the accepted answer to that question is not quite as definitive.
So, what is the type of a method reference in Java 8? Here's a little demonstration of how a method reference can be "cast" (lifted?) into a java.util.function.Function
:
package java8.lambda;
import java.util.function.Function;
public class Question {
public static final class Greeter {
private final String salutation;
public Greeter(final String salutation) {
this.salutation = salutation;
}
public String makeGreetingFor(final String name) {
return String.format("%s, %s!", salutation, name);
}
}
public static void main(String[] args) {
final Greeter helloGreeter = new Greeter("Hello");
identity(helloGreeter::makeGreetingFor)
.andThen(g -> "<<<" + g + ">>>")
.apply("Joe");
//Compilation error: Object is not a function interface
// Function
// .identity()
// .apply(helloGreeter::makeGreetingFor)
// .andThen(g -> "<<<" + g + ">>>")
// .apply("Joe");
Function
.<Function<String,String>>identity()
.apply(helloGreeter::makeGreetingFor)
.andThen(g -> "<<<" + g + ">>>")
.apply("Joe");
//Compilation error: Cannot resolve method 'andThen(<lambda expression>)'
// (helloGreeter::makeGreetingFor)
// .andThen(g -> "<<<" + g + ">>>")
// .apply("Joe");
// java.lang.invoke.LambdaMetafactory ???
}
private static <I,O> Function<I,O> identity(final Function<I,O> fun1) {
return fun1;
}
}
So, is there a less painful (more straight-forward) way of casting a method reference into a compiled/concrete type which can be passed around?
First of all, method references "are compact, easy-to-read lambda expressions for methods that already have a name" (see The Java Tutorials - Method References).
So in fact, you are asking for the type of a lambda expression. This is clearly explained in JLS §15.27.3 (Type of a Lambda Expression).
In short, there are three compatibilities mentioned:
- Assignment context
- Invocation context
- Casting context
The type of a lambda expression or a method reference is inferred by the compiler. As now several contexts can (and must) be taken into account, Java 8 came with big enhancements for type inference.
The only restriction for lambda expressions is that the inferred type must be a functional interface. In fact, equal lambda expressions can have different types regarding to their context.
From the JLS, section 15.13.2, "Type of a Method Reference":
A method reference expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.
...
If a method reference expression is compatible with a target type T, then the type of the expression, U, is the ground target type derived from T.
Basically, the type of the method reference is whatever the context expects it to be. Independent of a context, the method reference doesn't really have a type. There's no way to pass around a "raw" method reference and then turn it into a Function or Consumer or whatever at some later point.
Method references are just a syntactic sugar for a function that uses passed parameter as an input argument. So, you can assign them this way:
Runnable runnable = System.out::println;
Consumer consumer = System.out::println;
types are inferred and depend on a context.
Your case:
Function<String, String> foo = helloGreeter::makeGreetingFor;
and it is equal to:
Function<String, String> foo = s -> helloGreeter.makeGreetingFor(s);
If you just have a method reference helloGreeter::makeGreetingFor
, it has no type.
If you want to give a type to a method reference without assigning it or passing it as an argument (which assigns it to the parameter), you can cast it:
String greeting =
((Function<String, String>)helloGreeter::makeGreetingFor)
.apply("Joe");
来源:https://stackoverflow.com/questions/29875136/does-a-method-reference-in-java-8-have-a-concrete-type-and-if-so-what-is-it