Core OOP Principles & Dynamic Method Dispatch
Revisit object-oriented foundations. Understand encapsulation, inheritance trade-offs, virtual method tables (vtable), and dynamic polymorphism.
By the end, you’ll be able to…
- Contrast encapsulation, abstraction, inheritance, and runtime polymorphism.
- Understand the Virtual Method Table (vtable) and dynamic method dispatch mechanics.
- Apply the 'Composition over Inheritance' design principle.
01The Pillars of Object-Oriented Architecture
Object-Oriented Programming (OOP) organizes software into cohesive, self-contained entities called Objects that combine state (fields) and behavior (methods). The four classic pillars of OOP are Encapsulation, Abstraction, Inheritance, and Polymorphism.
Encapsulation hides internal state behind access modifiers (private, protected, public) and exposes controlled operations. Abstraction separates what an object does from how it accomplishes it. Inheritance allows code sharing, while Polymorphism allows different object types to respond to the same method invocation dynamically.
02Dynamic Method Dispatch and the vtable
How does the JVM know which method implementation to execute when you invoke account.processPayment(amount) on a parent reference? The answer is Dynamic Method Dispatch, mediated by the Virtual Method Table (vtable).
Every class loaded into memory possesses a vtable: an array of memory pointers pointing to its concrete method implementations. When a child class overrides a parent method, the child's vtable entry points to the overridden code. At runtime, invokevirtual performs an instant O(1) table offset lookup, executing the subclass code without expensive type-checking cascades.
+---------------------------------------------------+| vtable Dynamic Method Dispatch |+---------------------------------------------------+| Parent vtable: [ 0: toString ] [ 1: processPayment]-> DefaultImpl| Child vtable: [ 0: toString ] [ 1: processPayment]-> CryptoImpl| (invokevirtual queries index 1 of the concrete instance's vtable)|+---------------------------------------------------+03Composition Over Inheritance
While inheritance allows code reuse, deep inheritance hierarchies create tight coupling, fragile base class bugs, and unexpected side effects. If a parent class modifies an internal method, it can silently break overridden behavior in child subclasses.
The professional design principle is Composition over Inheritance: construct complex behaviors by assembling independent collaborator objects rather than extending classes. Use inheritance strictly when a genuine 'is-a' relationship exists with behavioral substitution (Liskov Substitution Principle).
04Why This Matters in Production
In production codebases with millions of lines of code, leaky encapsulation and fragile inheritance chains are the primary drivers of technical debt. When private fields are exposed via unvalidated getters and setters, external components mutate state arbitrarily, invalidating invariants.
By enforcing strict immutability, validating data inside constructors, and favoring composition, you create robust domain models that can be refactored safely without cascading failures.
05Production Architecture & Engineering Trade-Offs
In large-scale enterprise codebases, rigorous domain modeling relies on enforcing structural invariants at the type level rather than relying on defensive runtime validation. Modern Java leverages sealed types and records to turn runtime type errors into compile-time exhaustiveness guarantees. When designing domain transfer objects or persistent entities, ensuring immutability protects against unintended side effects across concurrent worker threads.
Furthermore, avoiding deep inheritance hierarchies in favor of composition and interface contracts eliminates fragile base class anti-patterns. By encapsulating state mutations within well-defined service boundaries and exposing pure functional transformers, software architects build maintainable, audit-compliant systems that scale smoothly across distributed cloud infrastructure.
The ideas worth keeping.
- Encapsulation defends domain invariants behind strict visibility barriers.
- Virtual method calls use vtable index lookups for O(1) dynamic dispatch.
- Favor composition over inheritance to build modular, maintainable enterprise systems.
Make it yours.
Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.
Exercise 1: Defending Domain Invariants
Design an immutable BankAccount record that validates non-negative balance and non-empty owner name inside a compact constructor.
Explore a worked solution
public record BankAccount(String owner, long balance) { public BankAccount { if (owner == null || owner.isBlank()) throw new IllegalArgumentException("Owner required"); if (balance < 0) throw new IllegalArgumentException("Balance cannot be negative"); }}Compact constructors in records validate parameters before field assignment, guaranteeing invariant safety.
Exercise 2: Refactoring Inheritance to Composition
Refactor a class SecureLogger extending FileLogger to instead wrap a Logger instance via composition.
Explore a worked solution
public class SecureLogger { private final Logger delegate; public SecureLogger(Logger delegate) { this.delegate = delegate; } public void log(String msg) { delegate.log(sanitize(msg)); } private String sanitize(String m) { return m.replaceAll("password=.*", "***"); }}Wrapping the delegate logger allows intercepting and sanitizing messages without coupling to base class internals.
What stayed with you?
Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.
Exercise 1: Defending Domain Invariants
Design an immutable BankAccount record that validates non-negative balance and non-empty owner name inside a compact constructor.
Build successful. (Java 15) [Process exited normally]
Keep exploring.
The official references behind this lesson, when you want a second angle or the full details.
One lesson closer.
Reading is a start. Trying it yourself makes the connection.