Static vs Dynamic Binding — Specification Deep-Dive¶
JLS §15.12 (method invocation), JVMS §6.5 (invoke* opcodes), JVMS §5.4.5 (method resolution), JLS §8.4.8 (override), JLS §8.3 (fields).
1. Where canonical text lives¶
| Topic | Source |
|---|---|
| Method invocation | JLS §15.12 |
| Compile-time method resolution | JLS §15.12.2 |
| Runtime method invocation | JLS §15.12.4 |
| Override rules | JLS §8.4.8 |
| Field access | JLS §15.11 |
invokevirtual | JVMS §6.5 |
invokeinterface | JVMS §6.5 |
invokestatic | JVMS §6.5 |
invokespecial | JVMS §6.5 |
invokedynamic | JVMS §6.5 |
| Method resolution | JVMS §5.4.5 |
getfield/putfield | JVMS §6.5 |
getstatic/putstatic | JVMS §6.5 |
2. JLS §15.12 — method invocation¶
The method invocation expression e.m(args) is evaluated:
- Evaluate
eto a target reference. - Evaluate
argsleft-to-right. - Resolve the method (compile time): determine the signature based on declared types.
- Invoke the method (runtime): for instance methods, dispatch via receiver's class.
JLS §15.12.4 details the runtime invocation:
If the form is
Primary.Identifier(...), the Primary is evaluated. If null, NPE. Otherwise, the method is dispatched.
For invokevirtual / invokeinterface, dispatch is dynamic.
3. JLS §15.11 — field access¶
The form
Primary.Identifier, where Identifier is a field, is evaluated by evaluating the Primary, then accessing the field declared in the static type of the Primary.
Field access is statically bound. The declared type wins, regardless of the actual class.
4. JLS §8.4.8 — override semantics¶
An override: - Same name and parameters as the parent (post-erasure). - Same or subtype return type (covariant). - Same or fewer/narrower checked exceptions. - Same or wider access.
The override participates in dynamic dispatch — calls land in the most-derived class's implementation.
5. JLS §8.4.8.2 — static method hiding¶
A static method declared in a subclass with the same signature as a static method in the superclass hides the parent's, but does not override it. Hiding does not affect dynamic dispatch.
Static methods are dispatched at compile time via declared type.
6. JVMS §6.5.invokevirtual¶
The opcode performs:
- Resolve method symbolic reference (link time, once).
- Verify accessibility.
- Pop receiver.
- Search receiver's class C for matching method: a. If C has a method with same (name, descriptor) and it's not abstract, use it. b. Otherwise, recursively search C's superclasses. c. If still not found, search C's superinterfaces (with maximally specific matching).
- Invoke the matched method.
Throws NullPointerException if receiver is null.
7. JVMS §6.5.invokeinterface¶
Similar to invokevirtual but the resolved class is an interface. The receiver searches its itable for the implementing method.
The instruction includes a count byte (historical; ignored by modern JVMs) and a final 0 byte (placeholder).
8. JVMS §6.5.invokestatic¶
1. Resolve method ref.
2. Verify the method is static.
3. Initialize the resolved class if needed.
4. Pop args (no receiver).
5. Invoke directly.
No receiver, no dispatch.
9. JVMS §6.5.invokespecial¶
Used for direct method invocation (no dynamic dispatch):
1. Resolve method ref.
2. Verify accessibility (the resolved method must be in the current class, an ancestor, or Object).
3. Pop receiver and args.
4. Invoke the method as resolved (NOT via vtable lookup).
For constructors, the resolved method must be a <init> of the same class or superclass.
10. JVMS §6.5.invokedynamic¶
1. On first invocation: call the bootstrap method to obtain a CallSite.
2. Subsequent calls: invoke the CallSite's target.
The CallSite is bound to a MethodHandle that performs the actual logic. After binding, the call is direct (or polymorphic, depending on the CallSite implementation).
11. JLS §15.12.4 — runtime method invocation¶
The full algorithm for dispatching e.m(args):
- Compute the actual class (from receiver).
- Use the resolved method's vtable index to look up the method on the actual class.
- Invoke that method with
thisbound to the receiver.
This is the formal specification of dynamic dispatch.
12. Field access: get/put opcodes¶
| Opcode | Use | Static or instance |
|---|---|---|
getfield | Read instance field | Instance |
putfield | Write instance field | Instance |
getstatic | Read static field | Static |
putstatic | Write static field | Static |
All four are dispatched based on the field's declared class (no polymorphism). The receiver (for getfield/putfield) is just popped from the stack.
13. Constant variables (JLS §4.12.4)¶
A final field of primitive or String type, initialized to a constant expression, is a constant variable. Reading it does NOT trigger class init and is inlined at the use site.
class A {
public static final int X = 5; // constant variable
public static final Integer Y = 5; // not (boxed)
public static final int Z = compute(); // not (non-constant init)
}
Constant variables have no field-access overhead — they're literals at the use site.
14. Reading order¶
- JLS §15.12 — method invocation
- JLS §15.11 — field access
- JLS §8.4.8 — override
- JVMS §6.5 — invoke* opcodes
- JVMS §5.4.5 — method resolution
- JVMS §6.5 — getfield/putfield/getstatic/putstatic
- JLS §4.12.4 — constant variables
Memorize this: JLS §15.12 + JVMS §6.5 govern method dispatch. invokevirtual/invokeinterface use vtable/itable (dynamic). invokestatic/invokespecial/invokedynamic are direct or bootstrap-resolved. Field access (get/put) is always static. Constant variables are inlined at compile time.