简体   繁体   中英

Method reference notation vs "standard" Lambda notation

I was expecting these to be simple drop-in replacements for each other, but they're not. Clearly I'm not understanding the notation.

Can anyone explain why does that happen?

playButton.setOnAction(e -> track.play());

Here, compiler is happy with play() having a signature of

void play()

but here

playButton.setOnAction(track::play);

it requires

void play(Event e)

Here is a quote from the Java language specification :

A method reference expression (§15.13) is potentially compatible with a functional interface type T if, where the arity of the function type of T is n , there exists at least one potentially applicable method when the method reference expression targets the function type with arity n (§15.13.1), and one of the following is true:

  • The method reference expression has the form ReferenceType :: [TypeArguments] Identifier and at least one potentially applicable method is either ( i ) static and supports arity n , or ( ii ) not static and supports arity n-1 .

  • The method reference expression has some other form and at least one potentially applicable method is not static.

...

The definition of potential applicability goes beyond a basic arity check to also take into account the presence and "shape" of functional interface target types .

Every method reference should conform to a function interface (which is an interface that declares a single abstract method, non-overriding methods from Object class). The compiler needs to verify whether the provided reference resolves to a single existing method that has required arity (number of parameters) and their types match the types of the method declared by the target functional interface .

Let's have a look at the prior Java 8 code ( code-sample from JavaFX tutorial by created Oracle ):

button2.setOnAction(new EventHandler<ActionEvent>() {
    @Override public void handle(ActionEvent e) {
        label.setText("Accepted");
    }
});

That is the "shape" that needs to be filled with a code describing an action. Method handle() of the EventHandler interface expects an event as an argument. Whether it would be used or not, that's up to you, the key point is that the abstract method of the target interface expects this argument to be provided .

By using a lambda expression e -> track.play() you're explicitly telling to ignore it.

And when you're passing a method reference track::play , which should be classified as a reference to an instance method of a particular object ( see ), the compiler will try to resolve it to a method play(Event) and you're getting a compilation error because it fails to find one.

In this case, reference track::play is not an equivalent of lambda e -> track.play() , but () -> track.play() , which doesn't conform to the target functional interface EventHandler .

In case if you wonder, how a method reference which can be applicable to a non-static method of arity n-1 mentioned in the specification ( see case ii ) can look like, here is an example:

BiPredicate<String, String> startsWith = String::startsWith; // the same as (str1, str2) -> str1.strarsWith(str2);

System.out.println(startsWith.test("abc", "a"));    // => true
System.out.println(startsWith.test("fooBar", "a")); // => false

And you can construct a similar reference which conforms to EventHandler interface and applicable an instance method of arity n-1 using one of the parameterless methods of the Event type. It's not likely to be useful in practice, but it would be valid from the compiler perspective of view, so feel free to try it as an exercise.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM