Does a method reference in Java 8 have a specific type, and if so, what is it?

This question is quite closely related to another . However, I feel that the accepted answer to this question is not entirely conclusive.

So what is a method reference type in Java 8? Here's a small demo of how a method reference can be "cast" (raised?) In 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 direct) way of casting a method reference into a compiled / concrete type that can be passed?

+3


source to share


4 answers


Method references are just syntactic sugar for a function that takes a passed parameter as an input argument. So you can assign them like this:

Runnable runnable = System.out::println;
Consumer consumer = System.out::println;

      

are deduced and context dependent.

Your case:



Function<String, String> foo = helloGreeter::makeGreetingFor;

      

and it is equal to:

Function<String, String> foo = s -> helloGreeter.makeGreetingFor(s);

      

+5


source


First of all, method references "are compact, easy-to-read lambda expressions for methods that already have a name" (see Java Tutorials - Method References ).

So you are asking for the type of the lambda expression. This is clearly explained in JLS §15.27.3 (Lambda Expression Type) .

In short, three compatibility is mentioned:



  • Destination context
  • call context
  • Casting context

The type of the lambda expression or method reference is inferred by the compiler. Since it is currently possible to account for (and should) account for multiple contexts, there are big improvements to type inference in Java 8.

The only limitation for lambda expressions is that the intended type must be a functional interface . In fact, equal lambda expressions can be of different types with respect to their context.

+5


source


From JLS, section 15.13.2, "Method Reference Type" :

An experimental method expression is compatible in the target context, invocation context, or caste context with the target type T if T is an interface functional type (§9.8), and the expression is congruent to the function type of the ground target type derived from T.

...

If the method reference expression is compatible with the target type T, then the type of the expression, U, is the type of the ground target type from T.

Basically, the type of the method reference is what the context expects. Regardless of the context, the method reference is really typeless. There is no way to pass a reference to a "raw" method and then turn it into a function or consumer or whatever at some later point.

+5


source


If you only have a method reference helloGreeter::makeGreetingFor

, it has no type.

If you want to specify the type of the method reference without assigning it, or pass it as an argument (which assigns it to a parameter), you can do it:

String greeting =
    ((Function<String, String>)helloGreeter::makeGreetingFor)
        .apply("Joe");

      

+1


source







All Articles