Dynamic Dispatch & Proxies — Interview Questions¶
Topic: Dynamic Dispatch & Proxies
Introduction¶
These questions test whether a candidate understands proxies as the metaprogramming technique of intercepting and synthesizing behavior — distinct from the low-level vtable dispatch of the runtime. Strong answers can explain JDK vs CGLIB/ByteBuddy proxies, the JS Proxy/Reflect pairing, Python __getattr__ vs __getattribute__, Ruby method_missing, and — above all — the self-invocation trap that silently disables proxy-based AOP.
Table of Contents¶
Conceptual¶
Question 1¶
What is a proxy, in the metaprogramming sense?
A stand-in object that intercepts operations (method calls, property access) performed on it and decides what to do — forward to a target, augment with extra behavior, block, or synthesize a response. It lets you add behavior to an object without modifying the object's own code.
Question 2¶
How is this different from the dynamic dispatch a vtable does?
Vtable dispatch is the runtime resolving which existing method to call on a virtual call — a fixed, compiler-generated mechanism. Proxy-based dispatch is metaprogramming: it intercepts the call to run arbitrary logic and may synthesize a method that was never statically defined. One selects among defined methods; the other manufactures or wraps behavior.
Question 3¶
Name the canonical use cases for proxies.
Cross-cutting concerns / AOP (logging, timing, transactions, caching, retry, security), lazy initialization/loading, remote proxies (RPC stubs), virtual and protection proxies, mocking in tests, and reactive/observable objects. The common thread: intercept access to inject behavior.
Question 4¶
What's the difference between a virtual proxy, a protection proxy, and a remote proxy?
A virtual proxy defers creation/loading of an expensive object until first use (lazy loading); a protection proxy enforces access control before delegating; a remote proxy represents an object in another process/machine, turning calls into messages (RPC stubs). All three share the "stand-in that intercepts" shape.
Language-Specific¶
Question 5¶
Java: JDK dynamic proxy vs CGLIB/ByteBuddy — when is each used?
JDK java.lang.reflect.Proxy proxies interfaces only, routing every call through an InvocationHandler.invoke. CGLIB/ByteBuddy generate a subclass at runtime, so they can proxy concrete classes (no interface needed) but can't override final/ private/static methods. Spring uses JDK proxies when the bean is interface-typed and CGLIB/ByteBuddy when it's class-typed.
Question 6¶
JS: what are Proxy and Reflect, and why are they used together?
Proxy wraps a target with traps (get, set, has, deleteProperty, apply, construct, …) that intercept operations. Reflect provides the default implementation of each operation, so inside a trap you call Reflect.get(...) to do the normal thing and add your behavior around it — augmenting rather than reimplementing. Vue 3 reactivity is built on this.
Question 7¶
Python: __getattr__ vs __getattribute__?
__getattr__ is called only when normal attribute lookup fails (the attribute is missing) — ideal for synthesizing attributes/methods on demand. __getattribute__ is called on every attribute access, is far more powerful and far more dangerous (easy infinite recursion — you must delegate to super().__getattribute__), and is rarely needed.
Question 8¶
Ruby: how does method_missing implement dynamic dispatch?
When a message isn't matched by a defined method, Ruby calls method_missing(name, *args), letting the object synthesize behavior (ActiveRecord find_by_email). It must be paired with respond_to_missing? so respond_to? and Method objects reflect the dynamic methods honestly.
Question 9¶
Go has no method_missing/proxy interception. How do Go programs get similar results?
Through interfaces plus struct embedding/composition (promoted methods) and explicit wrapper types, plus a bit of reflect for generic tooling. The lack of dynamic interception is deliberate — Go favors explicit, statically-checkable code over runtime magic.
Tricky / Trap¶
Question 10¶
Your @Transactional annotation on a method isn't taking effect. Most likely cause?
Self-invocation: the method is called via this.method() from another method of the same bean, so the call bypasses the Spring proxy and the transactional advice never runs. Same for @Cacheable/@Async. Fix by crossing a proxy boundary (self-injection, restructuring) or using AspectJ weaving.
Question 11¶
Why might a @Transactional on a final or private method silently do nothing?
A CGLIB/ByteBuddy subclass proxy overrides methods to add advice, but it can't override final, private, or static methods — so there's no interception point and the advice is silently skipped.
Question 12¶
A Hibernate-loaded entity fails an instanceof SomeClass check or equals. Why?
It's a lazy-loading proxy (a generated subclass), not a direct instance, so identity- and type-sensitive operations can behave unexpectedly; instanceof against the concrete class or naive equals/== may fail. Use the entity's interface, Hibernate.unproxy, or id-based equals.
Question 13¶
Why is overriding __getattribute__ to add logging dangerous?
It runs on every attribute access, so any self.x inside it re-invokes it → infinite recursion (you must call super().__getattribute__('x')), and it adds cost to all access. __getattr__ (missing-only) is almost always the right hook.
Question 14¶
What's the cost of method_missing beyond performance?
It swallows typos: a misspelled method name silently routes to method_missing (e.g. a dynamic-finder attempt) instead of raising NoMethodError, hiding bugs. And without respond_to_missing?, reflection lies about which methods exist.
Design¶
Question 15¶
Design a logging/timing proxy that wraps any service interface. What mechanism per language?
Java: Proxy.newProxyInstance with an InvocationHandler that times around method.invoke(target, args) (interfaces) or ByteBuddy for classes. JS: a Proxy with an apply/get-returns-wrapped-function trap using Reflect. Python: __getattr__ returning a wrapper, or a class decorator. Keep it coarse-grained (service boundary), preserve identity/signature where possible, and ensure exceptions propagate unwrapped.
Question 16¶
You need reactive objects (re-run code when a field changes). Proxy or explicit setters? Trade-offs.
A Proxy (Vue 3 style) is transparent — plain property assignment triggers reactivity, including new properties and array indices — but adds per-access trap cost and can surprise on identity/serialization. Explicit setters/signals (Solid, MobX actions) are more verbose but more predictable and faster. Choose Proxy for ergonomic transparency, explicit for performance and clarity; many frameworks offer both shallow and deep modes to bound the cost.
In this topic
- interview
- tasks