OOP Fundamentals
Polymorphism
Static and dynamic dispatch
Polymorphism (meaning "many forms") is the OOP capability that allows a single interface or reference type to represent different underlying implementations. Polymorphism enables developers to write code that interacts with abstractions, letting the runtime environment determine the concrete behaviors.
1. Learning Objectives
- Differentiate between Static Polymorphism (method overloading) and Dynamic Polymorphism (method overriding).
- Understand dynamic binding and how virtual method tables (vtables) resolve calls at runtime.
- Write cleanly overloaded methods and overridden structures to handle dynamic states.
2. Problem Statement
Without polymorphism, writing code that handles multiple types of engines or drivers results in bloated, conditional logic. If your system queries both MySQL and Postgres databases, you are forced to write repetitive if-else blocks:
if (dbType == MYSQL) {
queryMySql(sql);
} else if (dbType == POSTGRES) {
queryPostgres(sql);
}
Adding a third database forces you to modify every client method in the application, violating the Open/Closed Principle.
3. Real-world Analogy
Think of a computer keyboard. Every keyboard has a "Backspace" key.
If you are writing text inside a text editor, pressing Backspace deletes a character. If you are inside a web browser, pressing Backspace might take you to the previous page. If you are inside a video player, it might skip back 10 seconds.
The hardware button interaction is identical (the interface is standard), but the system yields different behaviors depending on the active application context.
4. Theory
Polymorphism occurs in two distinct forms:
- Static (Compile-Time) Polymorphism: Achieved via Method Overloading (methods in the same class share the same name but have different parameter signatures). The compiler resolves which method to invoke at compile-time.
- Dynamic (Runtime) Polymorphism: Achieved via Method Overriding (subclasses provide specific implementations for methods defined in parent classes/interfaces). The virtual machine resolves which method to execute at runtime.
5. Visual Diagrams (UML & Memory structures)
Class Diagram
QueryExecutor
Object Diagram
Memory Diagram
Stack frame reference
Heap space (Overriding Object)
Type: PostgresExecutor
vtable pointer: @PostgresVtable
Object Lifecycle
Polymorphism occurs during the object's active usage stage, routing method execution dynamically based on the concrete heap type resolved when the instruction executes.
6. Syntax Explanation
- Java: Overloads methods by changing parameters or types (return types alone do not suffice). Overrides parent methods using the
@Overrideannotation. - Python: Lacks compile-time method overloading (last method definition wins). Dynamic overrides are resolved via Python's dynamic type protocol.
- C++: Methods must be declared with the
virtualkeyword to enable overriding and dynamic dispatch. Overloaded methods are resolved statically during compilation.
7. Step-by-Step Implementation
Let's build a polymorphic Database Query Executor:
- Step 1: Declare the base interface
QueryExecutorwithexecuteQuery(String sql). - Step 2: Implement concrete class
MySqlExecutoroverriding the base method. - Step 3: Add static overloading methods inside
MySqlExecutorto support query timeouts or pagination. - Step 4: Write main client logic to show static and dynamic resolution.
8. Complete Code (Mini Project)
9. Code Walkthrough
In overloading, the compiler evaluates parameter configurations to link the target method signature statically. In overriding, the interface reference variables (QueryExecutor) trigger virtual checks, dynamically routing requests to the subclass method at runtime.
10. Execution Flow
- The compiler links overloaded methods at compile time based on parameter types and counts.
- Instantiate a concrete subclass.
- Invoke method overriding dynamically. The virtual machine checks vtable redirects to execute the concrete subclass method.
11. Internal Working
At runtime, the virtual machine resolves dynamic dispatch via a vtable (Virtual Table). The compiler inserts a hidden pointer (vptr) into the base class. When a subclass is loaded, the vtable is populated with the memory addresses of the overridden methods, enabling fast runtime lookups.
12. Complexity Analysis
- Time Complexity: $O(1)$ for both static overload binding and dynamic vtable dispatch redirection.
- Space Complexity: $O(1)$ constant memory allocation for the vtable.
13. Best Practices
- Always use @Override annotation: Prevents silent bugs caused by typos in overridden method signatures.
- Respect Liskov Substitution Principle: Overridden methods must accept identical parameter constraints and return types as defined in the base class.
14. Common Mistakes
- Attempting to overload a method by changing only its return type (this triggers compilation errors).
- Forgetting to declare virtual destructors in C++ base classes, resulting in memory leaks when deleting subclasses.
15. Interview Questions
Q: How does the compiler resolve overloaded methods?
Answer: The compiler uses "static binding." It analyzes the number, order, and type of parameters passed to the method at compile-time to link the call to the correct method signature.
16. Practice Exercises
- Easy: Add a subclass
PostgresExecutoroverridingexecuteQuery(). - Medium: Add an overloaded option to
MySqlExecutorthat accepts a transaction isolation level enum. - Hard: Create a database connection pool router class that accepts a list of
QueryExecutorsubclasses and routes queries dynamically based on connection load.
17. Challenge Problem
Design a polymorphic file compressor framework (COMPRESSOR base, ZIP child, GZIP child) supporting both stream-based overloading and dynamic algorithm selections.
18. Summary
- Polymorphism lets a single interface represent multiple concrete implementations.
- Static polymorphism (overloading) is resolved at compile-time based on parameters.
- Dynamic polymorphism (overriding) is resolved at runtime using vtable lookups.
19. Cheat Sheet
| Property | Method Overloading | Method Overriding |
|---|---|---|
| Resolution Phase | Compile-Time (Static) | Runtime (Dynamic) |
| Method Signature | Same name, different parameters | Same name, identical parameters |
| Class Scope | Within a single class | Across parent-child relationships |
20. Quiz
1. Which form of polymorphism is resolved at compile-time?
A) Method Overriding
B) Method Overloading (Correct)
C) Interface inheritance
2. What structure resolves overridden method calls at runtime?
A) Stack frame local lists
B) Virtual Method Table (vtable) (Correct)
C) PC Register stack pointers
3. Can you overload a method by changing only the return type in Java?
A) Yes, return types are part of method signatures
B) No, changes in return types alone trigger compilation errors (Correct)
C) Only if using generic types
4. What keyword enables method overriding in C++?
A) extends
B) virtual (Correct)
C) override
5. Which of the following is true about dynamic polymorphism?
A) It resolves methods faster than static polymorphism
B) It routes method execution dynamically based on the concrete instance type on the heap (Correct)
C) It prevents class inheritance
6. What happens when a final method is declared in a Java base class?
A) Subclasses cannot override it (Correct)
B) It cannot be overloaded
C) It causes memory compilation errors
7. What is the space complexity of storing vtable references per class?
A) O(N)
B) O(1) (Correct)
C) O(log N)
8. Which principle is violated if a subclass method throws unexpected exceptions that break base contracts?
A) Single Responsibility Principle
B) Liskov Substitution Principle (Correct)
C) Dependency Inversion Principle
9. In overloading, what does static binding mean?
A) Bindings are locked to static classes
B) Resolution is fixed during compilation based on the static reference types (Correct)
C) Resolution only works on static methods
10. Where is the virtual pointer (vptr) stored in C++?
A) Inside stack frames
B) Inside class instance memory blocks on the heap (Correct)
C) Inside Metaspace constant registers
21. Next Lesson Preview
Congratulations! You have completed the OOP Fundamentals module. In the next module, Class Relationships, we begin with Association to study how classes connect and reference each other in system design!