3

Code:

@FunctionalInterface
interface VoidSupplier {
    void apply() throws Exception;
}

void execute(VoidSupplier voidSupplier) {
    if (voidSupplier != null) {
        try {
            voidSupplier.apply();
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

Call execute use lambda:

@Test
public void testLambda() {
    InputStream i = null;
    execute(() -> i.close());       // use lambda
    System.out.println("output some message");  // will be executed
}

Call execute use method reference:

@Test
void testMethodReference() {
    InputStream i = null;
    execute(i::close);             //  use method reference
    System.out.println("output some message");   // will not be executed
}

When use lambda, execute(VoidSupplier) will be executed first, and then execute () -> i.close().
but use method reference, i::close will be executed first, and then execute execute(VoidSupplier).
Why lambda and method reference have different execution timing?

Guo
  • 1,761
  • 2
  • 22
  • 45
  • 2
    Can you clarify what you mean by "why"? What kind of answers are you looking for? Will quotes from the JLS satisfy you? Or are you looking for the benefits of designing the language this way? – Sweeper Aug 23 '21 at 06:33
  • @Sweeper Why lambda and method reference have different execution timing? – Guo Aug 23 '21 at 06:50
  • @Guo how do you measure performance? on the same machine? same CPU time allocation? same runtime environment? what's the time difference? huge? little? I think your questions lacks details. – Giorgi Tsiklauri Aug 23 '21 at 07:00

1 Answers1

5

execute(VoidSupplier voidSupplier) is a method. Prior to executing this method, its argument must be evaluated, regardless of whether it's a lambda expression or a method reference.

The lambda expression snippet:

In order for the lambda expression to be evaluated, there's no need to execute the lambda expression body. Therefore, the expression () -> i.close() doesn't throw an exception. Only when execute is executed, NullPointerException is thrown when voidSupplier.apply() is executed, and you catch that exception, allowing the System.out.println to be executed.

The method reference snippet:

If you use a null reference as the left side of a method reference, the evaluation of the method reference will result in NullPointerException:

J.L.S. 15.3.3. Run-Time Evaluation of Method References:

First, if the method reference expression begins with an ExpressionName or a Primary, this subexpression is evaluated. If the subexpression evaluates to null, a NullPointerException is raised, and the method reference expression completes abruptly. If the subexpression completes abruptly, the method reference expression completes abruptly for the same reason.

This means evaluating i::close throws NullPointerException, and execute() is not executed. Since you don't catch this exception, your System.out.println statement is never reached.

Eran
  • 387,369
  • 54
  • 702
  • 768