Reflection · ch. 11, p. 85
The Reflection API
Reflection inspects and invokes types discovered at runtime. It powers every framework you use, and it costs you compile-time safety, performance, and refactoring.
Class<?> type = Class.forName("com.example.Service");
Object instance = type.getDeclaredConstructor().newInstance();
Method m = type.getDeclaredMethod("execute", String.class);
m.setAccessible(true);
Object result = m.invoke(instance, "payload");
getX vs getDeclaredX
getMethods() returns public members including inherited ones.
getDeclaredMethods() returns everything declared on that exact class,
including private, but nothing inherited. Confusing the two is the most common
reflection bug.
Costs
- Nothing is checked until runtime; a rename breaks it silently.
invokeboxes arguments into anObject[]and wraps any thrown exception inInvocationTargetException— always unwrap withgetCause().- Reflective calls resist inlining, though the modern JIT narrows the gap.
- Since Java 9,
setAccessibleon another module’s internals is refused unless that module opens the package. This is why older libraries broke on 9+.
Reading generics back
Erasure removes type arguments from values but keeps signatures in the class file, so declared types remain readable:
Type t = field.getGenericType();
if (t instanceof ParameterizedType pt) {
Type arg = pt.getActualTypeArguments()[0]; // e.g. String
} 2 questions on this concept Drill it