From Reflection to Modern Java (Part 1): Understanding the Trade-offs
Last week, I gave an online talk about this topic. I called it something like “Refactoring a Payment Engine with Java.” The goal was to show a legacy system built using heavy reflection, and how we can refactor it using modern Java features—comparing both approaches side by side.
That’s also why I didn’t post anything recently.
This is a topic you don’t usually see in tutorials or beginner courses: Reflection.
But you’re probably already using it.
Frameworks like Spring, ArchUnit, and others rely heavily on reflection… so indirectly, you do too =D
What is Reflection?
Some solid definitions from well-known sources:
“Reflection is the ability of a program to manipulate as data something representing the state of the program during its own execution.” [1]
“Reflection is the ability of a process to examine, introspect, and modify its structure and behavior.” [2]
“Reflection is the integral ability for a program to observe or change its own code as well as all aspects of its programming language […] even at runtime.” [3]
Think of it like this: your program can make decisions at runtime about what to execute.
But how does that look in Java?
String methodName = "validateLimits"; // could come from DB, config, API...
Method method = ValidationMethods.class
.getDeclaredMethod(methodName, PaymentRequest.class);
method.invoke(validationMethods, request);This is a classic example.
I’m calling a method from ValidationMethods without knowing which one at compile time.
Why did we use this?
There are several reasons why reflection was widely used:
Inspecting code at runtime
Creating dynamic configurations
Making systems easier to extend
If your project uses AspectJ, you’re already benefiting from techniques like reflection to solve cross-cutting concerns.
👉 Why do you use reflection? Let me know.
The Trade-offs
Nothing is free, right?
Once you start using reflection, you also introduce some problems:
Performance issues
You lose JIT optimizations because reflective calls are rarely inlined.
Breaking encapsulation
You can access private fields, which makes the code harder to maintain.
Loss of compile-time safety
Errors move to runtime. In our example, the compiler has no idea if validateLimits actually exists.
Harder tooling support
Modern IDEs struggle to track usage, making the system harder to understand.
Can you think of other downsides?
Let’s make it practical
Here’s the starting repository:
👉 (add your repo link here)
Do you want to give it a shot and try to refactor it?
In the next article, I’ll show how we can replace reflection using modern Java features—without losing flexibility.
Stay tuned.
Happy coding!
References
[1] “Reflective programming,” Wikipedia. [Online]. Available: https://en.wikipedia.org/wiki/Reflective_programming
[2] D. G. Bobrow, R. P. Gabriel, and J. L. White, CLOS in Context: The Shape of the Design Space, 2004. [Online]. Available: https://dreamsongs.com/Files/clos-book.pdf
[3] J. Malenfant, M. Jacques, and F.-N. Demers, A Tutorial on Behavioral Reflection and its Implementation, Université de Montréal, 1996. [Online]. Available: https://web.archive.org/web/20170821214626/http://www2.parc.com/csl/groups/sda/projects/reflection96/docs/malenfant/malenfant.pdf


Of course the link is not there!
But here it is
Do you want download the source?
https://github.com/brunobaiano/payment-service-refactoring/releases/tag/phase1-article-v1.0
Clone?
git clone --branch phase1-article-v1.0 https://github.com/brunobaiano/payment-service-refactoring.git
you can try to run tests
cd payment-service-refactoring
mvn -pl phase1-legacy-reflection clean test
Great post, Bruno! Studying for the OCP 21, I noticed that modern Java (11+) now favors "list.toArray(new T[0])" over "new T[list.size()]" as the performance standard. It’s a curious trade-off: we gain performance through JVM intrinsics, but we move away from static compile-time control by delegating allocation to internal reflection (a small but real trade-off). We're essentially trading explicit code for runtime optimization. Slightly disappointing, but it is what it is!