Call Methods at Runtime Using Java Reflection

1. Overview

In this short article, we’ll take a quick look at how to invoke methods at runtime using the Java Reflection API.

2. Getting Ready

Let’s create a simple class which we’ll use for the examples that follow:

public class Operations {
    public double publicSum(int a, double b) {
        return a + b;
    }

    public static double publicStaticMultiply(float a, long b) {
        return a * b;
    }

    private boolean privateAnd(boolean a, boolean b) {
        return a && b;
    }

    protected int protectedMax(int a, int b) {
        return a > b ? a : b;
    }
}

3. Obtaining a Method Object

First, we need to get a Method object that reflects the method we want to invoke. The Class object, representing the type in which the method is defined, provides two ways of doing this.

3.1. getMethod()

We can use getMethod() to find any public method, be it static or instance that is defined in the class or any of its superclasses.

It receives the method name as the first argument, followed by the types of the method’s arguments:

Method sumInstanceMethod
  = Operations.class.getMethod("publicSum", int.class, double.class);

Method multiplyStaticMethod
  = Operations.class.getMethod(
    "publicStaticMultiply", float.class, long.class);

3.2. getDeclaredMethod()

We can use getDeclaredMethod() to get any method defined in the class. This includes public, protected, default access, and even private methods but excludes inherited ones.

It receives the same parameters as getMethod():

Method andPrivateMethod
  = Operations.class.getDeclaredMethod(
    "privateAnd", boolean.class, boolean.class);
Method maxProtectedMethod
  = Operations.class.getDeclaredMethod("protectedMax", int.class, int.class);

4. Invoking Methods

With the Method instance in place, we can now call invoke() to execute the underlying method and get the returned object.

4.1. Instance Methods

To invoke an instance method, the first argument to invoke() must be an instance of Method that reflects the method being invoked:

@Test
public void givenObject_whenInvokePublicMethod_thenCorrect() {
    Method sumInstanceMethod
      = Operations.class.getMethod("publicSum", int.class, double.class);

    Operations operationsInstance = new Operations();
    Double result
      = (Double) sumInstanceMethod.invoke(operationsInstance, 1, 3);

    assertThat(result, equalTo(4.0));
}

4.2. Static Methods

Since these methods don’t require an instance to be called, we can pass null as the first argument:

@Test
public void givenObject_whenInvokeStaticMethod_thenCorrect() {
    Method multiplyStaticMethod
      = Operations.class.getDeclaredMethod(
        "publicStaticMultiply", float.class, long.class);

    Double result
      = (Double) multiplyStaticMethod.invoke(null, 3.5f, 2);

    assertThat(result, equalTo(7.0));
}

5. Method Accessibility

By default, not all reflected methods are accessible. This means that the JVM enforces access control checks when invoking them.

For instance, if we try to call a private method outside its defining class or a protected method from outside a subclass or its class’ package, we’ll get an IllegalAccessException:

@Test(expected = IllegalAccessException.class)
public void givenObject_whenInvokePrivateMethod_thenFail() {
    Method andPrivateMethod
      = Operations.class.getDeclaredMethod(
        "privateAnd", boolean.class, boolean.class);

    Operations operationsInstance = new Operations();
    Boolean result
      = (Boolean) andPrivateMethod.invoke(operationsInstance, true, false);

    assertFalse(result);
}

@Test(expected = IllegalAccessException.class)
public void givenObject_whenInvokeProtectedMethod_thenFail() {
    Method maxProtectedMethod
      = Operations.class.getDeclaredMethod(
        "protectedMax", int.class, int.class);

    Operations operationsInstance = new Operations();
    Integer result
      = (Integer) maxProtectedMethod.invoke(operationsInstance, 2, 4);

    assertThat(result, equalTo(4));
}

By calling setAccesible(true) on a reflected method object, the JVM suppresses the access control checks and allows us to invoke the method without throwing an exception:

@Test
public void givenObject_whenInvokePrivateMethod_thenCorrect() {
    // ...
    andPrivateMethod.setAccessible(true);
    // ...
    Boolean result
      = (Boolean) andPrivateMethod.invoke(operationsInstance, true, false);

    assertFalse(result);
}

@Test
public void givenObject_whenInvokeProtectedMethod_thenCorrect() {
    // ...
    maxProtectedMethod.setAccessible(true);
    // ...
    Integer result
      = (Integer) maxProtectedMethod.invoke(operationsInstance, 2, 4);

    assertThat(result, equalTo(4));
}

6. Conclusion

In this quick article, we’ve seen how to call instance and static methods of a class at runtime through reflection. We also showed how to change the accessible flag on the reflected method objects to suppress Java access control checks when invoking private and protected methods.

As always, the example code can be found over on Github.

Leave a Reply

Your email address will not be published.