user081608
user081608

Reputation: 1113

Pass static methods in Java

I am trying to create a class that times the average runtime of a method. I understand how to do that by running it 100 times and taking the average of it all. Example:

private long calculateAvg(){
    long totalTime = 0;

    for(int i = 0; i < ITERATIONS; i++){
        long startTime = System.nanoTime();
        testMethod();
        long endTime = System.nanoTime();

        totalTime += (endTime - startTime);  //divide by 1000000 to get milliseconds.
    }
    return (totalTime / ITERATIONS);

}

Now I can set this to work for one static method but is there a way to pass different static methods into this to calculate instead of creating one of these for each method I want to test? If not, is there a design pattern that may work here? As of now, I am creating one of these methods for every other method I want to time and it doesn't seem efficient because I am reusing so much code.

Upvotes: 4

Views: 10153

Answers (5)

Balloon Wen
Balloon Wen

Reputation: 121

Based on your input, your method should be something like this

private long calculateAvg(Testable t){
  long totalTime = 0;

  for(int i = 0; i < ITERATIONS; i++){
    long startTime = System.nanoTime();
    t.testMethod();
    long endTime = System.nanoTime();

    totalTime += (endTime - startTime);  //divide by 1000000 to get milliseconds.
  }
  return (totalTime / ITERATIONS);
}

Then, you should have an interface with a single method:

public interface Testable {
    public void testMethod();
}

In your main method, you have 3 ways to pass the method. Let's say you want to test a static sort method in SelectionSort class.

First: anonymous class (the traditional way)

calculateAvg(new Testable(){
    @Override
    public void testMethod(){
       SelectionSort.sort();
    }
})

Second: Lambda (Java 8)

calculateAvg(() -> SelectionSort.sort())

Third: Method Reference Java 8 Method Reference: How to Use it

calculateAvg(SelectionSort::sort)

Upvotes: 0

Dave Pateral
Dave Pateral

Reputation: 1475

I got two solutions may help for this question:
1. Java reflection:

public long calculateAvg(Object obj, String methodName) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException{
    Method method = obj.getClass().getDeclaredMethod(methodName);
    method.setAccessible(true);

    long totalTime = 0;
    for(int i = 0; i < ITERATIONS; i++) {
        long startTime = System.nanoTime();
        method.invoke(obj);
        long endTime = System.nanoTime();

        totalTime += (endTime - startTime);  //divide by 1000000 to get milliseconds.
    }
    return (totalTime / ITERATIONS);
}
  1. Proxy Pattern

You can learn about proxy pattern, you don't need to call the real method yourself, the proxy does. you can get the time before and after calling the real method. proxy pattern tutorials

Upvotes: 1

shizhz
shizhz

Reputation: 12501

No you cann't pass method in Java because methods are not first class citizen in Java, they are not values. The pattern to resolve this kind of problem is to use anonymous class. Take your case as example, you can define an interface:

@FunctionalInterface  // this annotation is available since jdk1.8
public interface Measurable {
    void measure();
}

And another MeasureUtil util class, methods inside it accept the type Measurable:

    public class MeasureUtil {
    private static final long ITERATIONS = 1000;

    public static long calculateAvg(Measurable measurable) {
        long totalTime = 0;

        for(int i = 0; i < ITERATIONS; i++){
            long startTime = System.nanoTime();
            measurable.measure();
            long endTime = System.nanoTime();

            totalTime += (endTime - startTime);  //divide by 1000000 to get milliseconds.
        }
        return (totalTime / ITERATIONS);
    }
}

Then you new an instance of Measurable and pass it to MeasureUtil.calculateAvg each time:

    public class Main {
    public static void main(String[] args) {
        long avg = MeasureUtil.calculateAvg(new Measurable() {
            @Override
            public void measure() {
                method_under_mesure();
            }
        });
        System.out.println("Avg: " + avg);
    }

    private static void method_under_mesure() {
        System.out.println("call me");
    }
}

Since jdk1.8 Java starts to support lambda expression, which can make it much simpler and easier with the syntax sugar:

long avg = MeasureUtil.calculateAvg(() -> method_under_mesure());

Upvotes: 1

Code-Apprentice
Code-Apprentice

Reputation: 83527

You cannot "pass methods", static nor non-static. Instead, you need to create an object reference created from a class which contains the method. Then you call the method on the object reference. In order to provide different implementations of a method, you create an interface. Then you can have many classes which implement that interface and the method which it declares. Now your timing code uses that interface to access the correct method.

public interface TheMethod {
    public void foo();
}

public class Method1 implements TheMethod {
    public void foo() {
        // code goes here 
    }
}

public class Method2 implements TheMethod {
    public void foo() {
        // code goes here 
    }
}

Now modify your timing method to accept a TheMethod instance:

private long calculateAvg(TheMethod method){
    // ...
    method.foo();
    // ...
}

Finally, you can call this method with different instances of classes which implement TheMethod:

TheMethod m1 = new Method1();
TheMethod m2 = new Method2();

long x = calculateAvg(m1);
long y = calculateAvg(m2);

Upvotes: 3

shmosel
shmosel

Reputation: 50716

You can accept and run a Runnable argument:

private long calculateAvg(Runnable r) {
    //...
    long startTime = System.nanoTime();
    r.run();
    long endTime = System.nanoTime();
    //...
}

And call it like this:

long avg1 = calculateAvg(() -> testMethod());
long avg2 = calculateAvg(() -> testMethod2());

You might also want to consider some tips from How do I write a correct micro-benchmark in Java?

Upvotes: 4

Related Questions