DEV Community

ライフポータル
ライフポータル

Posted on • Originally published at code-izumi.com

Why is Java Reflection "Forbidden"? Disadvantages and the Right Use Cases

Java provides a powerful feature called Reflection, which allows for the dynamic manipulation of classes and methods at runtime.

However, in many professional development environments and coding standards, you will often see a rule: "The use of Reflection is, in principle, forbidden." Why is a standard feature of the language treated with such caution? In this article, we will dive deep into why Java Reflection is often deprecated or restricted, verify the performance impact through benchmarks, and explore the correct scenarios where Reflection is actually necessary.

Why is Reflection "Forbidden" or "Deprecated" in Java?

Reflection offers extreme flexibility, but that flexibility comes at a high cost. Most projects restrict its use for the following four reasons:

1. Loss of Compile-Time Type Safety

Java’s greatest strength is being a statically typed language. Normally, typos in method names or mismatches in argument types are caught as compile-time errors, allowing developers to fix them before the application even runs.

With Reflection, however, you specify class and method names as strings. This bypasses the compiler’s safety checks. If a method name is changed or an argument type is modified during a refactor, you won't realize the code is broken until the moment it crashes at runtime. This makes Reflection a breeding ground for bugs.

2. Significant Performance Overhead

Operations involving Reflection are considerably slower than normal method calls. This is because the JVM (Java Virtual Machine) cannot apply its usual optimizations like Inlining or JIT compilation. The dynamic resolution of type information requires significant processing overhead.

3. Destruction of Encapsulation

Reflection allows you to call setAccessible(true), which gives you forced access to private fields and methods from the outside. This destroys the principle of Encapsulation and risks causing unpredictable behavior by ignoring the original class designer’s intent.

4. Poor Readability and Maintainability

Code using Reflection tends to be verbose and complex. It is hard to tell which method of which class is being called just by looking at the code. Furthermore, IDEs often fail to track these calls, making future refactoring or debugging extremely difficult.


Performance Benchmark: Reflection vs. Normal Calls

To see exactly how much slower Reflection is, let’s compare a normal method call with a reflective one using a simple benchmark.

Benchmark Sample Code

import java.lang.reflect.Method;

public class ReflectionBenchmark {
    public static void main(String[] args) throws Exception {
        TargetObject target = new TargetObject();
        long iterations = 100_000_000;

        long startNormal = System.currentTimeMillis();
        for (long i = 0; i < iterations; i++) {
            target.doSomething();
        }
        long endNormal = System.currentTimeMillis();
        System.out.println("Normal Call: " + (endNormal - startNormal) + "ms");

        Method method = TargetObject.class.getMethod("doSomething");
        long startReflect = System.currentTimeMillis();
        for (long i = 0; i < iterations; i++) {
            method.invoke(target);
        }
        long endReflect = System.currentTimeMillis();
        System.out.println("Reflection Call: " + (endReflect - startReflect) + "ms");
    }
}

class TargetObject {
    public void doSomething() {
        int a = 1 + 1;
    }
}
Enter fullscreen mode Exit fullscreen mode

Benchmark Results (Estimation)

Call Type Execution Time (Approx.)
Normal Call 15ms
Reflection Call 450ms

As shown in this benchmark, Reflection can be dozens of times slower than a standard method call. While a few microseconds of difference might be negligible for a single operation, it becomes a critical bottleneck in high-frequency processing or real-time systems. This is the primary reason why Reflection is often forbidden in performance-sensitive logic.


When Reflection is Actually Necessary

Despite its drawbacks, Reflection remains an indispensable technology in the Java ecosystem. While application developers should avoid using it for business logic, it is actively utilized in the following scenarios:

1. Framework and Library Development

Major Java frameworks like Spring Framework, Hibernate, and JUnit rely heavily on Reflection. Core features such as Dependency Injection (DI) and Object-Relational Mapping (ORM)—where classes are not known until runtime—can only be achieved through these reflective capabilities.

2. Testing (JUnit)

In unit testing, you might occasionally need to verify the behavior of private methods. While it is generally better to test through public interfaces, Reflection is permitted when maintaining legacy code where you must trigger internal logic directly.

3. General-Purpose Tools

Reflection is useful when creating tools that handle objects without knowing their structure beforehand. For instance, a generic tool that takes any object and exports its fields to a CSV file requires Reflection to introspect the object's metadata at runtime.


Restrictions Since Java 9 (The Module System)

With the introduction of the Module System (Project Jigsaw) in Java 9, reflective access to internal JDK APIs has been strictly limited. Accessing internal classes now often requires explicit command-line flags like --add-opens. This shift represents Java's move toward stronger encapsulation and better security, making "illegal" reflective access a thing of the past.


Industry Coding Standards

Major corporate and security standards treat Reflection with extreme caution to ensure code reliability and safety:

Google Java Style Guide

While not an outright ban, Google emphasizes readability and simplicity. It discourages unnecessary metaprogramming, favoring plain Java code that is easy for humans and IDEs to track.

Security Standards (IPA)

Security organizations warn against the use of setAccessible(true) because it can bypass access control checks, potentially exposing sensitive data or creating vulnerabilities.

The fundamental rule remains: Always consider if you can solve the problem using Interfaces or Polymorphism before reaching for Reflection.


Summary

Reflection is a double-edged sword. While it provides the flexibility required for powerful frameworks, its misuse in standard application development leads to bugs, poor performance, and maintenance nightmares.

Avoid Reflection for business logic, prefer standard OOP principles like Polymorphism, and always understand the risks before bypassing compile-time safety.


Originally published at: [https://code-izumi.com/java/no-reflection-allowed/]

Top comments (0)