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.

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

>> GET ACCESS NOW

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

>> GET ACCESS NOW

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

1. Overview

While methods are made private in Java to prevent them from being called from outside the owning class, we may still need to invoke them for some reason.

To achieve this, we need to work around Java's access controls. This may help us reach a corner of a library or allow us to test some code that should normally remain private.

In this short tutorial, we'll look at how we can verify the functionality of a method regardless of its visibility. We'll consider two different approaches: the Java Reflection API and Spring's ReflectionTestUtils.

2. Visibility Out of Our Control

For our example, let's use a utility class LongArrayUtil that operates on long arrays. Our class has two indexOf methods:

public static int indexOf(long[] array, long target) {
    return indexOf(array, target, 0, array.length);
}

private static int indexOf(long[] array, long target, int start, int end) {
    for (int i = start; i < end; i++) {
        if (array[i] == target) {
            return i;
        }
    }
    return -1;
}

Let's assume that the visibility of these methods cannot be changed, and yet we want to call the private indexOf method.

3. Java Reflection API

3.1. Finding the Method with Reflection

While the compiler prevents us from calling a function that is not visible to our class, we can invoke functions via reflection. First, we need to access the Method object that describes the function we want to call:

Method indexOfMethod = LongArrayUtil.class.getDeclaredMethod(
  "indexOf", long[].class, long.class, int.class, int.class);

We have to use getDeclaredMethod in order to access non-private methods. We call it on the type that has the function, in this case, LongArrayUtil, and we pass in the types of the parameters to identify the correct method.

The function may fail and throw an exception if the method does not exist.

3.2. Allow the Method to Be Accessed

Now we need to elevate the method's visibility temporarily:

indexOfMethod.setAccessible(true);

This change will last until the JVM stops, or the accessible property is set back to false.

3.3. Invoke the Method with Reflection

Finally, we call invoke on the Method object:

int value = (int) indexOfMethod.invoke(
  LongArrayUtil.class, someLongArray, 2L, 0, someLongArray.length);

We have now successfully accessed a private method.

The first argument to invoke is the target object, and the remaining arguments need to match our method's signature. As in this case, our method is static, and the target object is the parent class – LongArrayUtil. For calling instance methods, we'd pass the object whose method we're calling.

We should also note that invoke returns Object, which is null for void functions, and which needs casting to the right type in order to use it.

4. Spring ReflectionTestUtils

Reaching internals of classes is a common problem in testing. Spring's test library provides some shortcuts to help unit tests reach classes. This often solves problems specific to unit tests, where a test needs to access a private field which Spring might instantiate at runtime.

First, we need to add the spring-test dependency in our pom.xml:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.4</version>
    <scope>test</scope>
</dependency>

Now we can use the invokeMethod function in ReflectionTestUtils, which uses the same algorithm as above, and saves us writing as much code:

int value = ReflectionTestUtils.invokeMethod(
  LongArrayUtil.class, "indexOf", someLongArray, 1L, 1, someLongArray.length);

As this is a test library, we wouldn't expect to use this outside of the test code.

5. Considerations

Using reflection to bypass function visibility comes with some risks and may not even be possible. We ought to consider:

  • Whether the Java Security Manager will allow this in our runtime
  • Whether the function we're calling, without compile-time checking, will continue to exist for us to call in the future
  • Refactoring our own code to make things more visible and accessible

6. Conclusion

In this article, we looked at how to access private methods using the Java Reflection API and using Spring's ReflectionTestUtils.

As always, the example code for this article 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 next Friday:

>> GET ACCESS NOW

Generic footer banner
Comments are closed on this article!