When would you choose MethodHandles or VarHandle over the classic reflection API for dynamic invocation and field access?
answer
- Reflect to discover; MethodHandle/VarHandle to invoke fast
- MethodHandle: typed, invokeExact, inlinable, composable, access checked at lookup
- VarHandle: typed field access + volatile/acquire-release/CAS, replaces Unsafe
- Classic reflection: best for enumeration/annotations/config
- Lambdas use invokedynamic+MethodHandle, not reflection
basics
~20 sUse classic reflection (Method/Field/Constructor) for occasional, flexible introspection like config parsing or scanning annotations. Use MethodHandles and VarHandle when you call/access repeatedly on a hot path and need speed, because they're strongly typed, JIT-inlinable, and (VarHandle) give controlled memory ordering for fields.
solid answer
~50 sThe classic java.lang.reflect API (Method.invoke, Field.get/set, Constructor.newInstance) is the most flexible and discoverable, and it's right for cold, infrequent, introspection-heavy work — scanning annotations, building objects from config, test tooling. Its downsides are per-call overhead (boxing into Object[], access checks) and weak JIT optimization. MethodHandles (java.lang.invoke), introduced for invokedynamic, are strongly typed handles: with invokeExact they avoid boxing and inline almost like direct calls, so they win on repeated hot-path invocation, and they compose (bind, filter, adapt arguments). VarHandle is the typed successor to reflective field access and to sun.misc.Unsafe: besides plain get/set it offers volatile, acquire/release, and atomic compare-and-set access modes with defined memory semantics. The trade-off is that MethodHandle/VarHandle are harder to use (exact type signatures, MethodType, lookups) and less convenient for ad-hoc introspection. Rule of thumb: reflect to discover, MethodHandle/VarHandle to invoke fast and repeatedly.
code
java · 7 linesMethodHandles.Lookup l = MethodHandles.lookup();
// MethodHandle: typed dynamic call (access checked here, at lookup)
MethodHandle mh = l.findVirtual(String.class, "length", MethodType.methodType(int.class));
int len = (int) mh.invokeExact("hello"); // 5, no boxing
// VarHandle: atomic field access
VarHandle vh = l.findVarHandle(Counter.class, "value", int.class);
vh.compareAndSet(counter, 0, 1); // atomic CAS with defined memory semanticsgo deeper
Knows MethodHandles and VarHandle exist as alternatives to classic reflection and are generally faster.
Can state that MethodHandle is typed/inlinable for hot calls and VarHandle gives volatile/atomic field access.
Chooses correctly per use case, understands lookup-time access checks, invokeExact vs invoke, and VarHandle access modes.
Designs systems that reflect-once-then-build-handles/codegen, reasons about JIT inlining, memory semantics, security (Lookup), and migration off Unsafe.
## Three families of dynamic access in Java Java has, over time, grown three ways to do things dynamically. Knowing *when* each fits is a senior/principal-level concern. ### 1. Classic reflection — `java.lang.reflect` `Class`, `Method`, `Field`, `Constructor`. You **discover** members by name (`getDeclaredMethod`, `getDeclaredField`) and then act on them (`invoke`, `get`/`set`, `newInstance`). - **Strengths:** maximally flexible and *discoverable* — you can enumerate everything (`getDeclaredMethods()`), read annotations, inspect modifiers, build things generically. Ideal for **introspection**. - **Weaknesses:** per-call cost (arguments boxed into an `Object[]`, results boxed, access checks), and the call target is opaque to the JIT so it inlines poorly. Best for **infrequent / cold** work. ### 2. `MethodHandle` — `java.lang.invoke` Introduced to support `invokedynamic` (the bytecode behind lambdas). A `MethodHandle` is a **strongly typed, directly executable reference** to a method, constructor, or field accessor. You obtain one through a `Lookup` (`MethodHandles.lookup().findVirtual(Class, name, MethodType)`), where `MethodType` describes the exact return and parameter types. - **Strengths:** **performance** — `invokeExact` requires the call signature to match precisely, avoids boxing, and the JIT can **inline** it nearly like a direct call. They also **compose**: you can partially apply arguments (`bindTo`), reorder/insert/filter arguments, and build adapters. This is why they back lambda metafactories and high-performance frameworks. - **Weaknesses:** ergonomically harder — you must get the `MethodType` exactly right (`invokeExact` throws `WrongMethodTypeException` otherwise), and they're poor for *enumerating* members. They **invoke**, they don't **discover**. - **Access model:** access is checked **once, at lookup time**, against the `Lookup`'s permissions — a cleaner security model than per-call `setAccessible`. ### 3. `VarHandle` — typed field/array access (Java 9+) A `VarHandle` is the modern, **safe replacement for reflective field access and for the old internal `sun.misc.Unsafe`**. Obtained via `MethodHandles.lookup().findVarHandle(...)`, it represents a variable (instance field, static field, or array element). - **Strengths:** beyond plain `get`/`set` it offers **access modes with defined memory semantics**: `getVolatile`/`setVolatile`, `getAcquire`/`setRelease`, and **atomic** operations like `compareAndSet`, `getAndAdd`. This brings safe, standardized low-level concurrency primitives to ordinary fields — previously only possible via `Unsafe` or `AtomicXxxFieldUpdater`. Typed, so no boxing for primitive access. - **Weaknesses:** same exact-type strictness as `MethodHandle`; overkill for simple introspection. ## Decision guide | Need | Choose | |---|---| | Enumerate members, read annotations, parse config once | **classic reflection** | | Call the same dynamic method millions of times, low overhead | **MethodHandle** (`invokeExact`) | | Compose/adapt/partially-apply a call target | **MethodHandle** | | Atomic / volatile / ordered access to a field, replace `Unsafe` | **VarHandle** | | Inject a value into a private field once at startup | classic reflection is fine; VarHandle if hot | ## The principle **Reflect to discover; MethodHandle/VarHandle to invoke fast.** Classic reflection optimizes for *flexibility and introspection*; the `java.lang.invoke` family optimizes for *speed, type-safety, and (VarHandle) memory semantics* at the cost of ergonomics. A well-designed framework often *uses reflection once* at setup to discover members, then *builds MethodHandles/VarHandles (or generated code)* to do the actual repeated work — getting both flexibility and performance. From this you can derive why lambdas compile to `invokedynamic`+MethodHandle rather than reflection, and why concurrency utilities migrated from `Unsafe`/field updaters to `VarHandle`.
- What does VarHandle offer that reflective Field.get/set does not?Access modes with defined memory semantics: volatile/acquire-release reads and writes plus atomic operations like compareAndSet and getAndAdd. It is the safe, standard replacement for sun.misc.Unsafe and AtomicFieldUpdaters.
- When is MethodHandle's access check performed compared to setAccessible-based reflection?Once, at lookup time, against the Lookup object's permissions. Classic reflection re-checks per call unless you call setAccessible(true). The lookup-time model is generally cleaner and safer.
saying these in an interview costs you the question
- Claiming MethodHandle is just a faster alias for Method.invoke with no semantic differences (it requires exact MethodType, checks access at lookup, composes).
- Using MethodHandle/VarHandle for one-off introspection where classic reflection is simpler.
- Thinking VarHandle is only about speed — its main value is defined memory ordering and atomics.
- Assuming invoke (not invokeExact) gives the inlinable fast path — invoke does asType conversions and boxing.