Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is method reference operator accepted where Runnable Instance is expected?

I recently came across below sample code where method reference operator is used to reference a method call. There are 2 questions for which i am looking for an answer 1. The execute method where its called expects Runnable class. This code compiles file and gives no error why (App class is not Runnable)? 2. if I replace executorService.execute(app::someMethod); with executorService.execute(app.someMethod()); it give an compile error why?

public class Temp {

private static final Logger LOGGER = LoggerFactory.getLogger(Temp.class);

/**
 * @param args the command line arguments - not used
 */`enter code here`
public static void main(String... args) {
    final App app = new App();
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    for (int i = 0; i < 3; i++) {
        executorService.execute(app::someMethod);
    }
    executorService.shutdown();
    try {
        executorService.awaitTermination(10, TimeUnit.SECONDS);
    } catch (InterruptedException ie) {
        LOGGER.error("ERROR: Waiting on executor service shutdown!");
    }
}

}
class App {
     public void someMethod() {
           // Some logic
     }
}
like image 832
Chintan Avatar asked Jan 02 '23 07:01

Chintan


2 Answers

ExecutorService.execute expects an argument of type Runnable.

To answer your last question first... App.someMethod() is a void method (i.e. it doesn't return anything). As app.someMethod()'s return type is void, and as the void type cannot be assigned to a variable or method argument of type Runnable, you are getting a compilation error.

Now, as to your first question... The Runnable interface is a functional interface because it declares a single abstract method (SAM):

@FunctionalInterface
public interface Runnable {

    void run();
}

The SAM here is void run(), which doesn't receive any arguments and is a void method.

Now, the method reference app::someMethod is targeting the void someMethod() method of the App class, whose signature matches that one of the run method of the Runnable interface (by match I mean that both methods' return type is void and that neither one of them receives any argument).

So, when you pass the app::someMethod method reference as an argument to the executorService.execute method, the compiler safely transforms it to a Runnable instance.


EDIT: As user @MC Emperor highlights in the comments, the someMethod method of the App class may return something (i.e. it doesn't have to be necessarily a void method). In this case, as stated by the spec (see JLS § 15.27.3, thanks for the link!), the return value would be simply discarded.

This is because in Java, values returned by methods can be discarded, and method references (and also lambda expressions) honor this behavior.

like image 137
fps Avatar answered Jan 13 '23 15:01

fps


1) Yes, App class is not Runnable, but in your example it is not the App object that you are passing to execute method, but instead you do method reference there.

2) app::someMethod is a reference to an instance method of a particular object. app.someMethod() is just a call to someMethod() and it is expected that it returns an object implementing Runnable interface. It is not, that's why you get an error.

like image 38
mate00 Avatar answered Jan 13 '23 15:01

mate00