Is there any way to pass methods as a parameter?

Asked

Viewed 8,112 times

8

In Java, I can pass methods as parameters?

Example:

public class Teste {
    public String metodoA(){
        //faz alguma coisa
    }

    public void metodoB(double numero){
        //faz alguma coisa
    }

    public void metodoC(Metodo metodo, double numero){
        metodo(numero);
    }

    public void metodoD(Metodo metodo){
        metodo();
    }

    public static void main(String[] args) {
        Teste teste = new Teste();
        teste.metodoC(metodoA, 10.2);
        teste.metodoD(metodoB);
    }
}

Another example:

public double getRuntime(int[] vetor, Metodo metodo){
    this.initialTime = System.nanoTime();
    metodo(vetor);
    double tempo = System.nanoTime() - initialTime;
    return tempo;
}

Calling the method would be so:

getRuntime(vetor[], BubbleSort.bubbleSort());

The execution would be so(Varying the method to be called which in the case was bubbleSort()):

public double getRuntime(int[] vetor, Metodo metodo){
    this.initialTime = System.nanoTime();
    BubbleSort.bubbleSort(vetor);
    double tempo = System.nanoTime() - initialTime;
    return tempo;
}

Still confused and I learned that java does not allow the passage of methods as parameters. But what would be the alternatives to achieve this goal?

  • 2

    Pass the return of the method as paramtro, yes, now use a method as if it were an object, I believe not.

  • 1

    Try to explain better what you intend to do. In the method main() what are metodoA and metodoB? Are methods of the object teste?

  • 1

    Yes, methodoA and metodoB are test object methods.

3 answers

16


TL;DR

There are several ways to parameterize methods in Java:

  • The lowest level by reflection;
  • The traditional one using a specific interface;
  • Directly in the most modern form introduced in Java 8

Examples

Given the class below:

class Sorting {
    public static void staticSort(String[] vetor) {
        Arrays.sort(vetor);
    }
    public void sort(String[] vetor) {
        Arrays.sort(vetor);
    }
}

And the following vector of String:

String[] vetor = { "Maria", "José" };

Reflection

We can use the guy Method to receive the method reference, thus:

public static long executar(Method metodo, String[] vetor) {
    long initialTime = System.nanoTime();
    try {
        metodo.invoke(null, new Object[] { vetor });
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        throw new RuntimeException(e);
    }
    return System.nanoTime() - initialTime;
}

The method invoke of the instance of a method allows calling the referenced method. The first parameter is the object instance, but if the method is static just pass null and then an array with the parameters, which in this case is an array of String.

We get the reference to the method like this:

Method metodo = Sorting.class.getDeclaredMethod("staticSort", String[].class);

And the method call goes like this:

executar(metodo, vetor);

The problems of reflection include:

  • It can lead to runtime errors that are not caught at build time as the names of the methods are recovered using simple Strings.
  • Performance. Using reflection is much slower.
  • Verbose and "ugly" code, having to deal with several complicated exceptions.

Using interfaces

An indirect way to pass a method is to use specific interfaces for a particular use.

The most common example of this is threads that implement the interface Runnable. In fact, this interface can and is used in different applications that need to pass a method to be executed that neither receives nor returns value.

Anyway, you can create your own interface. Example:

interface Sorter {
    void sort(String[] vetor);
}

And then you can create as many implementations as you want. Example:

class BubbleSorter implements Sorter {
    @Override
    public void sort(String[] vetor) { ... } 
}

And the consumer method will be:

void executar(Sorter sorter, String[] vetor) { ... }

So the main method will be something like this:

Sorter bubbleSorter = new BubbleSorter();
executar(bubbleSorter, vetor);

The advantage of this approach is that it is efficient, makes code easy to understand and also extends with new types of implementations.

The downside is that it requires creating new interfaces for each type of method you want to parameterize.

This is a reasonably flexible approach as it allows any object to implement this interface, but not as flexible as you can only have one method sort by class and must have the same signature.

Java 8 Method References

It is not only from Lâmbdas that the Java 8 gained fame. This version also introduced references to methods.

There are three main ways of semantically representing method references, using the following interfaces:

  • Consumer: a consumer method that takes a parameter and returns nothing.
  • Supplier: a vendor that does not receive parameters and returns a value.
  • Function: a function that receives a parameter and returns a value.

There are other types of functional interfaces more specialized, but these are the most generic.

A generic execution method could then be written like this:

public static long executar(Consumer<String[]> metodo, String[] vetor) {
    long initialTime = System.nanoTime();
    metodo.accept(vetor);
    return System.nanoTime() - initialTime;
}

We use the interface Consumer, since our method only receives a vector of String and returns nothing.

The call to our execution method using the static sorting method described above would look like this:

executar(Sorting::staticSort, vetor);

If we want to refer to an instance method, just use the instance name before the operator ::, thus:

Sorting instancia = new Sorting();
executar(instancia::sort, vetor);

This approach is more flexible and superior semantically than previous editions.

Note that any method that is compatible with the interface used as a parameter can be passed as a reference. It doesn’t necessarily have to be the 3 interfaces I mentioned so. For example, if you use the interface Comparator as a parameter, you can reference any method that receives two parameters of the same type. You can use your own interfaces as well.

The limitation here is in the amount of parameters. If you want to pass multiple values, you will need to have an interface with the right amount of parameters with the same types. However, this limitation is essential for the program to be compiled safely, otherwise several errors would go unnoticed by the time of execution.

Considerations

In the above examples, I used a vector of String as type. This may seem limiting. However, remember that you can use generics to create functional interfaces that "force" the client code to pass a method that is a function, consumer or supplier respecting the type rules imposed by you.

Finally, if you want to print the vector and check the result, you can use this:

System.out.println(Arrays.stream(vetor).collect(Collectors.joining(", ")));

5

Generally speaking, directly, no.

It is possible to create a class, which can even be anonymous, that contains this method and then call the method of this class according to some convention adopted. It is also possible for a class to implement an interface with the method that needs to be passed and its implementation to be performed at the time of class instantiation.

This was widely adopted until Java 7 and is still by some programmers who don’t like the slightly more functional style that Java is now following.

In Java 8 the Amble were introduced and is a way to pass a code as a parameter, which is a simplification of the process, although it is not exactly what you want.

I’m not taking a chance on an example because I’ve never used this in Java. There’s a response in the OS that gives a good example. In link previous also has several ways to achieve this goal.

There are other options, like reflection, but I think these are better.

3

It won’t be quite what you want but I think it will be as close as you can get (without using Reflection).

Define an interface:

public interface Action<T>
{
    void execute(T valor);
}

For each method you want to run on getRuntime() create a class that implements this interface.
For example:

public class BubbleSortAction implements Action<int[]>{

    public void execute(int[] valor){
        BubbleSort.bubbleSort(valor);
    }
}

State the method getRuntime() in this way:

public <T> double getRuntime(T valor, Action<T> metodo){
    this.initialTime = System.nanoTime();
    metodo.execute(valor);
    double tempo = System.nanoTime() - initialTime;
    return tempo;
}

Call it that:

int[] vetor = {1,2,3,4};
BubbleSortAction bubbleSortAction = new BubbleSortAction();
getRuntime(vetor, bubbleSortAction);

Using Reflection would be something like that(I have not tested)

public double getRunTime(Class<?> classe, String metodo, Object... parametros) 
        throws  NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Class<?>[] parametrosClasse = new Class[parametros.length];
    for(int i = 0; i < parametros.length; i++){
        parametrosClasse[i] = parametros[i].getClass();
    }
    Method m = classe.getClass().getMethod(metodo, parametrosClasse);
    long initialTime = System.nanoTime();
    Object ret = m.invoke(classe, parametros);
    double tempo = System.nanoTime() - initialTime;
    return tempo;
}

To use:

    int[] vetor = {1,2,3,4};
    try {
        getRunTime(BubbleSort.class, "bubbleSort", vetor);
    } catch (NoSuchMethodException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

Notes:

  • You must be careful not to make a mistake in the order and type of parameters passed.
  • Not to be wrong in the name of the method.
  • The time it takes to execute a track method Reflection, is larger than when called normally.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.