Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass and invoke method references in Java

Let's say I have a class called Server, and I would like to allow others to write Plugins for it. Say Plugin is an Interface that extends Runnable and adds a single method: void init(...). It is the job of a plugin to collect data and send it to the server. When the time comes to send data to the server, however, how does it do this? Coming from C and C++ I am looking for a thinking along the lines of a function pointer. It seems to be possible in Java though I have not found examples outside the Java Standard Class Library.

How do I pass a method reference to the init method such that it can be stored by the Plugin, and then how do I invoke the method whenever the Plugin wants to send data? For now say that the desired Server method is: void sendData(Integer data).

For example:

// Inside Server
Plugin p = new PluginImplementation();
p.init(this::sendData);    

// Plugin init
public void init(?? sendMethod) {
    storedSendMethod = sendMethod;
    // ...
}

// Plugin run
public void run() {
    // ...
    storedSendMethod(x) // Sends data to server
    // ...
}
like image 568
Ephemera Avatar asked Aug 27 '15 03:08

Ephemera


People also ask

How do you pass a reference method in Java?

In order to pass the reference, we pass the object of the class in the place of the actual parameter and the formal parameter of a class object type has the same reference to each other that's why with the help of the formal parameter object of class any changes will be reflected in both objects formal and actual ...

Can you pass things by reference in Java?

Java is always Pass by Value and not pass by reference, we can prove it with a simple example. Let's say we have a class Balloon like below. And we have a simple program with a generic method to swap two objects, the class looks like below.

How does Java pass reference type arguments to methods?

Arguments in Java are always passed-by-value. During method invocation, a copy of each argument, whether its a value or reference, is created in stack memory which is then passed to the method.


2 Answers

Using java.util.function.Function we can pass a function as an argument to a method, and then use apply() to apply it to the relevant arguments. Here's an example:

import java.util.function.Function;

public class FunctionDemo {

    // we will pass a reference to this method
    public static Integer square(Integer x) {
        return x * x;
    }

    // this method accepts the function as an argument and applies it to the input: 5
    public static Integer doSomething(Function<Integer, Integer> func) {
        return func.apply(5);
    }

    public static void main(String[] args) {
        // and here's how to use it
        System.out.println(doSomething(FunctionDemo::square)); // prints 25
    }   
}

Additional version with multiple parameters (passed as an array):

public static Integer sum(Integer[] x) {
    Integer result = 0;
    for(int i = 0; i < x.length; i++)
        result += x[i];
    return result;
}

public static void main(String[] args) {
    Integer[] arr = {1,2,3,4,5};
    System.out.println(doSomething(Play::sum, arr));
}

public static Integer doSomething(Function<Integer[], Integer> func,
                                  Integer[] arr) {        
    return func.apply(arr);
}
like image 163
Nir Alfasi Avatar answered Sep 21 '22 11:09

Nir Alfasi


If the method is void sendData(Integer data) that corresponds to a consumer that takes an Integer and returns a void which is covered by the built in Consumer<Integer> interface which has an accept(Integer) method that will invoke your function when called.

So your code will look like this:

public void init(Consumer<Integer> sendMethod) {
    storedSendMethod = sendMethod;
    // ...
}

// Plugin run
 void run() {
    // ...
    storedSendMethod.accept(x) // Sends data to server
   // ...
}

As a sidenote, having an init method is probably a bad Java design. you are better moving the initialization to the constructor if possbile

Plugin p = new PluginImplementation( this::sendData);
like image 31
dkatzel Avatar answered Sep 22 '22 11:09

dkatzel