Platform & Advanced APIs

Reflection & Dynamic Proxies

Reflection examines and manipulates classes at runtime: enumerate members, read/write fields, invoke methods, construct instances from names. Frameworks live on it; application code should treat it as a last resort (EJ 65).
  • Class<?> is the entry: obj.getClass(), MyType.class, Class.forName(name)
  • getDeclaredFields/Methods/Constructors (all, this class) vs getFields/… (public, inherited)
  • setAccessible(true) bypasses access control — within module limits (JPMS opens)
  • Costs: no compile-time checking, slower dispatch, refactoring-invisible, module friction
  • Generic declarations survive erasure and are readable (getGenericType)
  • Proxy.newProxyInstance fabricates interface implementations at runtime — the AOP primitive
The framework pattern: instantiate by name, inject by field
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.

Dynamic proxy: one InvocationHandler, any interface
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.

Sources
  • Core Java, Volume I: Fundamentals (13th ed.)Ch. 5.10, 6.5 — Reflection; Proxies
  • Effective Java (3rd ed.)Item 65