-->

Can I pass a Method as parameter of another method

2020-03-01 18:28发布

问题:

I am trying to measure the execution time for several methods. so I was thinking to make a method instead of duplicate same code many times.

Here is my code:

private void MeasureExecutionTime(Method m)
{
    startTime = System.nanoTime();
    try
    {
        m();
    }
    finally
    {
        endTime = System.nanoTime();
    }
    elapsedTime = endTime - startTime;
    System.out.println("This takes " + elapsedTime + " ns.");
}

Suppose I have myMethod(), how can I use MeasureExecutionTime() to measure myMethod's execution time?

回答1:

Methods aren't first-class objects in Java, so they can't be passed as parameters. You could use wrap your method call in an annoymous class that extends e.g. the Runnable interface:

private void MeasureExecutionTime(Runnable r) {
    r.run();
}

...


MeasureExecutionTime(new Runnable() { public void run() { m(); } });


回答2:

the simplest way to do this is to pass an interface. So something like

public interface CommonFuncs {

   public void execute();
   ... more functions

}

and then in you can have methods that take CommonFuncs as an argument. The implementations of those methods can call the execute method.



回答3:

I agree with @hvgotcodes, But yes, it is possible, though you don't call it like m(), but using invoke (which means that you probably should pass another argument or two with it...).



回答4:

You are in the correct path, you need to pass the Method object, the target object on which to execute the method and the arguments it takes and then invoke it into your try catch, something like:

private void MeasureExecutionTime(Method m, Object target, Object args) 
                            throws IllegalArgumentException, 
                                   IllegalAccessException, 
                                   InvocationTargetException
{
    long startTime = System.nanoTime();
    long endTime;
    try
    {
        m.invoke(target, args);
    }
    finally
    {
        endTime = System.nanoTime();
    }
    long elapsedTime = endTime - startTime;
    System.out.println("This takes " + elapsedTime + " ns.");
}


回答5:

I have a object call Counter which basically looks like this:

private long count;
private long errors;
private long duration;
private long errorDuration;

With two "time" methods for timing methods that return something and other methods which are void.

public <T> T time(ReturningRunnable<T> runnable) {
    long start = currentTimeMillis();
    T result = null;
    try {
        result = runnable.run();
    } catch (Throwable ex) {
        errorDuration += currentTimeMillis() - start;
        errors++;
        throw runtime(ex);
    }
    duration += currentTimeMillis() - start;
    count++;
    return result;
}

public void time(Runnable runnable) {
    time(new RunnableRunner(runnable));
}

I chose to rethrow the exceptions as runtime exceptions (as i'm not a fan of checked exceptions) but you could just as well make your custom MyRunnable interface throw exceptions and just catch and re-throw them. Here's a usage of the above:

counter.time(new Runnable() {
    @Override
    public void run() {
        // Do something
    });

Or this, in the returning-value case:

return counter.time(new ReturningRunnable<Integer>() {
    @Override
    public Integer run() {
        return 1; // You get the idea
    });

I like this because my counter objects can be exposed over JMX and injected wherever i need them. You could do what you've asked with reflection, but i think that would be messy (IMHO).



回答6:

you may do smth like:

private void MeasureExecutionTime(Method m)
{

    startTime = System.nanoTime();
    try
    {
        m.invoke(classObj, args);
    }
    finally
    {
        int endTime = System.nanoTime();
    }
    elapsedTime = endTime - startTime;
    System.out.println("This takes " + elapsedTime + " ns.");
}