Expand Authors Top

If you have a few years of experience in the Java ecosystem and you’d like to share that with the community, have a look at our Contribution Guidelines.

Expanded Audience – Frontegg – Security (partner)
announcement - icon User management is very complex, when implemented properly. No surprise here.

Not having to roll all of that out manually, but instead integrating a mature, fully-fledged solution - yeah, that makes a lot of sense.
That's basically what Frontegg is - User Management for your application. It's focused on making your app scalable, secure and enjoyable for your users.
From signup to authentication, it supports simple scenarios all the way to complex and custom application logic.

Have a look:

>> Elegant User Management, Tailor-made for B2B SaaS

November Discount Launch 2022 – Top
We’re finally running a Black Friday launch. All Courses are 30% off until the end of this week:

>> GET ACCESS NOW

November Discount Launch 2022 – TEMP TOP (NPI)
We’re finally running a Black Friday launch. All Courses are 30% off until the end of this week:

>> GET ACCESS NOW

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

Firstly, 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 of the class or any of its superclasses.

Basically, 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 kind of method. 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));
}

5.1. AccessibleObject#setAccesible

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() throws Exception {
    Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
    andPrivatedMethod.setAccessible(true);

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

    assertFalse(result);
}

5.2. AccessibleObject#canAccess

Java 9 comes with a brand new way to check whether a caller can access a reflected method object.

For this purpose, it provides canAccess as a replacement for the deprecated method isAccessible​.

Let's see it in action:

@Test
public void givenObject_whenInvokePrivateMethod_thenCheckAccess() throws Exception {
    Operations operationsInstance = new Operations();
    Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
    boolean isAccessEnabled = andPrivatedMethod.canAccess(operationsInstance);
 
    assertFalse(isAccessEnabled);
 }

We can use canAccess to check if the caller already has access to the reflected method before setting the accessible flag to true with setAccessible(true).

5.3. AccessibleObject#trySetAccessible

trySetAccessible is another handy method that we can use to make a reflected object accessible.

The good thing about this new method is that it returns false if the access cannot be enabled. However, the old method setAccessible(true) throws InaccessibleObjectException when it fails.

Let's exemplify the use of the trySetAccessible method:

@Test
public void givenObject_whenInvokePublicMethod_thenEnableAccess() throws Exception {
    Operations operationsInstance = new Operations();
    Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
    andPrivatedMethod.trySetAccessible();
    boolean isAccessEnabled = andPrivatedMethod.canAccess(operationsInstance);
        
    assertTrue(isAccessEnabled);
}

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.

November Discount Launch 2022 – Bottom
We’re finally running a Black Friday launch. All Courses are 30% off until the end of this week:

>> GET ACCESS NOW

Generic footer banner
Comments are closed on this article!