Platform & Advanced APIs
Reflection & Dynamic Proxies
Class<?>is the entry:obj.getClass(),MyType.class,Class.forName(name)getDeclaredFields/Methods/Constructors(all, this class) vsgetFields/…(public, inherited)setAccessible(true)bypasses access control — within module limits (JPMSopens)- Costs: no compile-time checking, slower dispatch, refactoring-invisible, module friction
- Generic declarations survive erasure and are readable (
getGenericType) Proxy.newProxyInstancefabricates interface implementations at runtime — the AOP primitive
Class<?> cl = Class.forName(config.get("handler.class"));
Object handler = cl.getDeclaredConstructor().newInstance();
for (Field f : cl.getDeclaredFields()) {
if (f.isAnnotationPresent(Inject.class)) {
f.setAccessible(true); // needs the module opened to us
f.set(handler, container.lookup(f.getType()));
}
}This is dependency injection, ORM hydration, and serialization in miniature — reflection lets a library operate on classes it has never seen. The price list (EJ 65): exceptions move from compile time to runtime, IDE refactorings silently miss string class names, JIT optimizations weaken across reflective calls, and JPMS requires packages to be opens-ed for deep access. Application code usually has a better tool: interfaces, Lambdas, or plain polymorphism.
OrderService proxied = (OrderService) Proxy.newProxyInstance(
loader,
new Class<?>[] { OrderService.class },
(proxy, method, args) -> {
long t0 = System.nanoTime();
try {
return method.invoke(realService, args);
} finally {
metrics.record(method.getName(), System.nanoTime() - t0);
}
});Dynamic proxies implement interfaces only; proxying concrete classes needs bytecode generation (ByteBuddy/CGLIB — how Spring proxies @Transactional beans and Mockito mocks classes). For repeated reflective calls, MethodHandle (java.lang.invoke) offers a faster, typed alternative the JIT can inline — the machinery beneath invokedynamic.