Dynamic Method Dispatch in Java

Introduction

Imagine you're a restaurant manager with different types of chefs: a Pizza Chef, a Sushi Chef, and a Pastry Chef. When a customer orders "something special," each chef creates their own specialty dish. You don't need to know which specific chef will handle the order—you just know they'll all make "food."

Dynamic Method Dispatch in Java works exactly like this! It's the mechanism that allows Java to decide at runtime which method implementation to call when you have method overriding and inheritance. The JVM dynamically resolves the method call based on the actual object type, not the reference type.


What is Dynamic Method Dispatch?

Dynamic Method Dispatch is a core Java feature that enables runtime polymorphism. It allows the JVM to determine which overridden method to execute at runtime based on the actual object type, rather than the reference type.

Key Characteristics:

  • Runtime decision - Method resolution happens during execution
  • Based on object type - Not reference type
  • Enables polymorphism - Core of object-oriented programming
  • Works with method overriding - Requires inheritance hierarchy
  • Also called late binding - Binding happens at runtime

Code Explanation with Examples

Example 1: Basic Dynamic Method Dispatch

// Base class
class Animal {
// This method will be overridden
public void makeSound() {
System.out.println("Animal makes a sound");
}
// This method won't be overridden - to show the difference
public void sleep() {
System.out.println("Animal is sleeping");
}
}
// Derived classes
class Dog extends Animal {
// Override the makeSound method
@Override
public void makeSound() {
System.out.println("Dog barks: Woof! Woof!");
}
// Dog-specific method
public void fetch() {
System.out.println("Dog is fetching the ball");
}
}
class Cat extends Animal {
// Override the makeSound method
@Override
public void makeSound() {
System.out.println("Cat meows: Meow! Meow!");
}
// Cat-specific method
public void climb() {
System.out.println("Cat is climbing the tree");
}
}
class Cow extends Animal {
// Override the makeSound method
@Override
public void makeSound() {
System.out.println("Cow moos: Moo! Moo!");
}
}
public class BasicDynamicDispatch {
public static void main(String[] args) {
System.out.println("=== BASIC DYNAMIC METHOD DISPATCH ===");
// Create Animal references but assign different objects
Animal animal1 = new Dog();      // Animal reference, Dog object
Animal animal2 = new Cat();      // Animal reference, Cat object
Animal animal3 = new Cow();      // Animal reference, Cow object
Animal animal4 = new Animal();   // Animal reference, Animal object
// Dynamic method dispatch in action!
// The JVM decides at runtime which makeSound() to call
System.out.println("\n=== MAKING SOUNDS ===");
animal1.makeSound();  // Calls Dog's makeSound()
animal2.makeSound();  // Calls Cat's makeSound()
animal3.makeSound();  // Calls Cow's makeSound()
animal4.makeSound();  // Calls Animal's makeSound()
// Non-overridden method - always calls Animal's sleep()
System.out.println("\n=== SLEEPING ===");
animal1.sleep();  // Calls Animal's sleep() (not overridden)
animal2.sleep();  // Calls Animal's sleep() (not overridden)
animal3.sleep();  // Calls Animal's sleep() (not overridden)
animal4.sleep();  // Calls Animal's sleep() (not overridden)
// ❌ This won't work - reference type determines available methods
// animal1.fetch();  // Compilation error! Animal reference doesn't know about fetch()
// ✅ To call subclass-specific methods, we need casting
System.out.println("\n=== SUBCLASS-SPECIFIC METHODS ===");
if (animal1 instanceof Dog) {
Dog dog = (Dog) animal1;  // Explicit casting
dog.fetch();  // Now we can call Dog-specific method
}
if (animal2 instanceof Cat) {
Cat cat = (Cat) animal2;  // Explicit casting
cat.climb();  // Now we can call Cat-specific method
}
}
}

Output:

=== BASIC DYNAMIC METHOD DISPATCH ===
=== MAKING SOUNDS ===
Dog barks: Woof! Woof!
Cat meows: Meow! Meow!
Cow moos: Moo! Moo!
Animal makes a sound
=== SLEEPING ===
Animal is sleeping
Animal is sleeping
Animal is sleeping
Animal is sleeping
=== SUBCLASS-SPECIFIC METHODS ===
Dog is fetching the ball
Cat is climbing the tree

Example 2: Real-World Payment System

// Base payment class
abstract class Payment {
protected String paymentId;
protected double amount;
public Payment(String paymentId, double amount) {
this.paymentId = paymentId;
this.amount = amount;
}
// This method will be overridden - dynamic dispatch will decide which version to call
public abstract void processPayment();
// Common method - won't be overridden
public void validatePayment() {
System.out.println("Validating payment: " + paymentId);
System.out.println("Amount: $" + amount);
}
// Common method - won't be overridden
public void generateReceipt() {
System.out.println("Generating receipt for: " + paymentId);
}
}
// Concrete payment implementations
class CreditCardPayment extends Payment {
private String cardNumber;
private String expiryDate;
public CreditCardPayment(String paymentId, double amount, String cardNumber, String expiryDate) {
super(paymentId, amount);
this.cardNumber = cardNumber;
this.expiryDate = expiryDate;
}
@Override
public void processPayment() {
System.out.println("=== PROCESSING CREDIT CARD PAYMENT ===");
validatePayment();
System.out.println("Charging credit card: " + maskCardNumber(cardNumber));
System.out.println("Expiry: " + expiryDate);
System.out.println("Transaction completed successfully!");
generateReceipt();
}
private String maskCardNumber(String cardNumber) {
return "****-****-****-" + cardNumber.substring(cardNumber.length() - 4);
}
// Credit card specific method
public void applyRewardPoints() {
System.out.println("Applying reward points for credit card payment");
}
}
class PayPalPayment extends Payment {
private String email;
public PayPalPayment(String paymentId, double amount, String email) {
super(paymentId, amount);
this.email = email;
}
@Override
public void processPayment() {
System.out.println("=== PROCESSING PAYPAL PAYMENT ===");
validatePayment();
System.out.println("Redirecting to PayPal for: " + email);
System.out.println("PayPal transaction completed!");
generateReceipt();
}
// PayPal specific method
public void refundToPayPal() {
System.out.println("Initiating PayPal refund process");
}
}
class BankTransferPayment extends Payment {
private String accountNumber;
private String routingNumber;
public BankTransferPayment(String paymentId, double amount, String accountNumber, String routingNumber) {
super(paymentId, amount);
this.accountNumber = accountNumber;
this.routingNumber = routingNumber;
}
@Override
public void processPayment() {
System.out.println("=== PROCESSING BANK TRANSFER ===");
validatePayment();
System.out.println("Transferring funds to account: " + maskAccountNumber(accountNumber));
System.out.println("Routing: " + routingNumber);
System.out.println("Bank transfer initiated - may take 1-3 business days");
generateReceipt();
}
private String maskAccountNumber(String accountNumber) {
return "****" + accountNumber.substring(accountNumber.length() - 4);
}
}
public class PaymentSystemDemo {
public static void main(String[] args) {
System.out.println("=== PAYMENT SYSTEM WITH DYNAMIC DISPATCH ===");
// Create different payment methods using base class reference
Payment payment1 = new CreditCardPayment("PAY001", 150.00, "1234567812345678", "12/25");
Payment payment2 = new PayPalPayment("PAY002", 75.50, "[email protected]");
Payment payment3 = new BankTransferPayment("PAY003", 2000.00, "987654321", "021000021");
// Store payments in an array (polymorphism in action!)
Payment[] payments = {payment1, payment2, payment3};
// Process all payments - DYNAMIC DISPATCH determines which processPayment() to call
System.out.println("\n=== PROCESSING ALL PAYMENTS ===");
for (Payment payment : payments) {
payment.processPayment();  // Runtime decision!
System.out.println(); // Empty line for separation
}
// Demonstrate calling specific methods with casting
System.out.println("=== PAYMENT-SPECIFIC OPERATIONS ===");
if (payment1 instanceof CreditCardPayment) {
CreditCardPayment ccPayment = (CreditCardPayment) payment1;
ccPayment.applyRewardPoints();
}
if (payment2 instanceof PayPalPayment) {
PayPalPayment ppPayment = (PayPalPayment) payment2;
ppPayment.refundToPayPal();
}
// Using method that takes base class but works with all subclasses
System.out.println("\n=== BATCH PROCESSING ===");
processBatchPayments(payments);
}
// This method demonstrates the power of polymorphism
// It works with ANY Payment subclass without knowing the specific type
public static void processBatchPayments(Payment[] payments) {
System.out.println("Starting batch payment processing...");
for (int i = 0; i < payments.length; i++) {
System.out.println("Processing payment " + (i + 1) + ":");
payments[i].processPayment();  // Dynamic dispatch!
System.out.println("---");
}
System.out.println("Batch processing completed!");
}
}

Output:

=== PAYMENT SYSTEM WITH DYNAMIC DISPATCH ===
=== PROCESSING ALL PAYMENTS ===
=== PROCESSING CREDIT CARD PAYMENT ===
Validating payment: PAY001
Amount: $150.0
Charging credit card: ****-****-****-5678
Expiry: 12/25
Transaction completed successfully!
Generating receipt for: PAY001
=== PROCESSING PAYPAL PAYMENT ===
Validating payment: PAY002
Amount: $75.5
Redirecting to PayPal for: [email protected]
PayPal transaction completed!
Generating receipt for: PAY002
=== PROCESSING BANK TRANSFER ===
Validating payment: PAY003
Amount: $2000.0
Transferring funds to account: ****4321
Routing: 021000021
Bank transfer initiated - may take 1-3 business days
Generating receipt for: PAY003
=== PAYMENT-SPECIFIC OPERATIONS ===
Applying reward points for credit card payment
Initiating PayPal refund process
=== BATCH PROCESSING ===
Starting batch payment processing...
Processing payment 1:
=== PROCESSING CREDIT CARD PAYMENT ===
Validating payment: PAY001
Amount: $150.0
Charging credit card: ****-****-****-5678
Expiry: 12/25
Transaction completed successfully!
Generating receipt for: PAY001
---
Processing payment 2:
=== PROCESSING PAYPAL PAYMENT ===
Validating payment: PAY002
Amount: $75.5
Redirecting to PayPal for: [email protected]
PayPal transaction completed!
Generating receipt for: PAY002
---
Processing payment 3:
=== PROCESSING BANK TRANSFER ===
Validating payment: PAY003
Amount: $2000.0
Transferring funds to account: ****4321
Routing: 021000021
Bank transfer initiated - may take 1-3 business days
Generating receipt for: PAY003
---
Batch processing completed!

Example 3: Multi-level Inheritance with Dynamic Dispatch

// Multi-level inheritance hierarchy
class Employee {
protected String name;
protected double baseSalary;
public Employee(String name, double baseSalary) {
this.name = name;
this.baseSalary = baseSalary;
}
// This method will be overridden at different levels
public double calculateSalary() {
System.out.println("Calculating base salary for " + name);
return baseSalary;
}
public void displayInfo() {
System.out.println("Employee: " + name);
System.out.println("Base Salary: $" + baseSalary);
}
}
class Manager extends Employee {
private double bonus;
public Manager(String name, double baseSalary, double bonus) {
super(name, baseSalary);
this.bonus = bonus;
}
@Override
public double calculateSalary() {
System.out.println("Calculating manager salary for " + name);
double total = baseSalary + bonus;
System.out.println("Base: $" + baseSalary + ", Bonus: $" + bonus + ", Total: $" + total);
return total;
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.println("Bonus: $" + bonus);
System.out.println("Role: Manager");
}
// Manager-specific method
public void conductMeeting() {
System.out.println(name + " is conducting a team meeting");
}
}
class Developer extends Employee {
private int overtimeHours;
private double overtimeRate;
public Developer(String name, double baseSalary, int overtimeHours, double overtimeRate) {
super(name, baseSalary);
this.overtimeHours = overtimeHours;
this.overtimeRate = overtimeRate;
}
@Override
public double calculateSalary() {
System.out.println("Calculating developer salary for " + name);
double overtimePay = overtimeHours * overtimeRate;
double total = baseSalary + overtimePay;
System.out.println("Base: $" + baseSalary + ", Overtime: $" + overtimePay + ", Total: $" + total);
return total;
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.println("Overtime Hours: " + overtimeHours);
System.out.println("Overtime Rate: $" + overtimeRate);
System.out.println("Role: Developer");
}
// Developer-specific method
public void writeCode() {
System.out.println(name + " is writing code");
}
}
class SeniorDeveloper extends Developer {
private double techBonus;
public SeniorDeveloper(String name, double baseSalary, int overtimeHours, double overtimeRate, double techBonus) {
super(name, baseSalary, overtimeHours, overtimeRate);
this.techBonus = techBonus;
}
@Override
public double calculateSalary() {
System.out.println("Calculating senior developer salary for " + name);
double baseSalary = super.calculateSalary(); // Call parent's calculation
double total = baseSalary + techBonus;
System.out.println("Tech Bonus: $" + techBonus + ", Final Total: $" + total);
return total;
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.println("Tech Bonus: $" + techBonus);
System.out.println("Role: Senior Developer");
}
// Senior developer specific method
public void mentorJunior() {
System.out.println(name + " is mentoring junior developers");
}
}
public class MultiLevelInheritanceDemo {
public static void main(String[] args) {
System.out.println("=== MULTI-LEVEL INHERITANCE WITH DYNAMIC DISPATCH ===");
// Create employees at different hierarchy levels
Employee emp1 = new Employee("John Basic", 50000);
Employee emp2 = new Manager("Alice Manager", 70000, 15000);
Employee emp3 = new Developer("Bob Coder", 60000, 20, 50);
Employee emp4 = new SeniorDeveloper("Carol Expert", 80000, 10, 75, 10000);
// Store all in Employee array (polymorphism)
Employee[] employees = {emp1, emp2, emp3, emp4};
// Dynamic dispatch in action - different calculateSalary() called for each
System.out.println("\n=== CALCULATING SALARIES ===");
for (Employee emp : employees) {
System.out.println("---");
double salary = emp.calculateSalary(); // Runtime decision!
System.out.println("Final salary for " + emp.name + ": $" + salary);
}
System.out.println("\n=== DISPLAYING EMPLOYEE INFO ===");
for (Employee emp : employees) {
System.out.println("---");
emp.displayInfo(); // Runtime decision!
}
// Demonstrate accessing specific methods with casting
System.out.println("\n=== EMPLOYEE-SPECIFIC ACTIONS ===");
for (Employee emp : employees) {
System.out.println("---");
if (emp instanceof Manager) {
Manager manager = (Manager) emp;
manager.conductMeeting();
} else if (emp instanceof SeniorDeveloper) {
SeniorDeveloper senior = (SeniorDeveloper) emp;
senior.mentorJunior();
} else if (emp instanceof Developer) {
Developer dev = (Developer) emp;
dev.writeCode();
} else {
System.out.println(emp.name + " is performing general duties");
}
}
// Method that works with any Employee type
System.out.println("\n=== PERFORMANCE REVIEW ===");
conductPerformanceReview(employees);
}
// This method demonstrates the power of polymorphism
// It works with ANY Employee subclass without modification
public static void conductPerformanceReview(Employee[] employees) {
System.out.println("=== PERFORMANCE REVIEW SESSION ===");
for (Employee emp : employees) {
System.out.println("\nReviewing: " + emp.name);
emp.displayInfo();
double salary = emp.calculateSalary();
System.out.println("Approved salary: $" + salary);
}
System.out.println("=== REVIEW COMPLETED ===");
}
}

Output:

=== MULTI-LEVEL INHERITANCE WITH DYNAMIC DISPATCH ===
=== CALCULATING SALARIES ===
---
Calculating base salary for John Basic
Final salary for John Basic: $50000.0
---
Calculating manager salary for Alice Manager
Base: $70000.0, Bonus: $15000.0, Total: $85000.0
Final salary for Alice Manager: $85000.0
---
Calculating developer salary for Bob Coder
Base: $60000.0, Overtime: $1000.0, Total: $61000.0
Final salary for Bob Coder: $61000.0
---
Calculating senior developer salary for Carol Expert
Calculating developer salary for Carol Expert
Base: $80000.0, Overtime: $750.0, Total: $80750.0
Tech Bonus: $10000.0, Final Total: $90750.0
Final salary for Carol Expert: $90750.0
=== DISPLAYING EMPLOYEE INFO ===
---
Employee: John Basic
Base Salary: $50000.0
---
Employee: Alice Manager
Base Salary: $70000.0
Bonus: $15000.0
Role: Manager
---
Employee: Bob Coder
Base Salary: $60000.0
Overtime Hours: 20
Overtime Rate: $50.0
Role: Developer
---
Employee: Carol Expert
Base Salary: $80000.0
Overtime Hours: 10
Overtime Rate: $75.0
Role: Developer
Tech Bonus: $10000.0
Role: Senior Developer
=== EMPLOYEE-SPECIFIC ACTIONS ===
---
John Basic is performing general duties
---
Alice Manager is conducting a team meeting
---
Bob Coder is writing code
---
Carol Expert is mentoring junior developers
=== PERFORMANCE REVIEW ===
=== PERFORMANCE REVIEW SESSION ===
Reviewing: John Basic
Employee: John Basic
Base Salary: $50000.0
Calculating base salary for John Basic
Approved salary: $50000.0
Reviewing: Alice Manager
Employee: Alice Manager
Base Salary: $70000.0
Bonus: $15000.0
Role: Manager
Calculating manager salary for Alice Manager
Base: $70000.0, Bonus: $15000.0, Total: $85000.0
Approved salary: $85000.0
Reviewing: Bob Coder
Employee: Bob Coder
Base Salary: $60000.0
Overtime Hours: 20
Overtime Rate: $50.0
Role: Developer
Calculating developer salary for Bob Coder
Base: $60000.0, Overtime: $1000.0, Total: $61000.0
Approved salary: $61000.0
Reviewing: Carol Expert
Employee: Carol Expert
Base Salary: $80000.0
Overtime Hours: 10
Overtime Rate: $75.0
Role: Developer
Tech Bonus: $10000.0
Role: Senior Developer
Calculating senior developer salary for Carol Expert
Calculating developer salary for Carol Expert
Base: $80000.0, Overtime: $750.0, Total: $80750.0
Tech Bonus: $10000.0, Final Total: $90750.0
Approved salary: $90750.0
=== REVIEW COMPLETED ===

Example 4: Dynamic Dispatch with Interfaces

// Interface defining contract for all shapes
interface Shape {
double calculateArea();
double calculatePerimeter();
void displayInfo();
}
// Concrete implementations
class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
@Override
public double calculatePerimeter() {
return 2 * Math.PI * radius;
}
@Override
public void displayInfo() {
System.out.println("Circle with radius: " + radius);
System.out.println("Area: " + calculateArea());
System.out.println("Perimeter: " + calculatePerimeter());
}
// Circle-specific method
public void drawCircle() {
System.out.println("Drawing a perfect circle");
}
}
class Rectangle implements Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double calculateArea() {
return width * height;
}
@Override
public double calculatePerimeter() {
return 2 * (width + height);
}
@Override
public void displayInfo() {
System.out.println("Rectangle: " + width + " x " + height);
System.out.println("Area: " + calculateArea());
System.out.println("Perimeter: " + calculatePerimeter());
}
// Rectangle-specific method
public boolean isSquare() {
return width == height;
}
}
class Triangle implements Shape {
private double side1;
private double side2;
private double side3;
public Triangle(double side1, double side2, double side3) {
this.side1 = side1;
this.side2 = side2;
this.side3 = side3;
}
@Override
public double calculateArea() {
// Using Heron's formula
double s = (side1 + side2 + side3) / 2;
return Math.sqrt(s * (s - side1) * (s - side2) * (s - side3));
}
@Override
public double calculatePerimeter() {
return side1 + side2 + side3;
}
@Override
public void displayInfo() {
System.out.println("Triangle with sides: " + side1 + ", " + side2 + ", " + side3);
System.out.println("Area: " + calculateArea());
System.out.println("Perimeter: " + calculatePerimeter());
}
// Triangle-specific method
public String getTriangleType() {
if (side1 == side2 && side2 == side3) return "Equilateral";
if (side1 == side2 || side1 == side3 || side2 == side3) return "Isosceles";
return "Scalene";
}
}
public class InterfaceDynamicDispatch {
public static void main(String[] args) {
System.out.println("=== DYNAMIC DISPATCH WITH INTERFACES ===");
// Create shapes using interface reference
Shape shape1 = new Circle(5.0);
Shape shape2 = new Rectangle(4.0, 6.0);
Shape shape3 = new Triangle(3.0, 4.0, 5.0);
Shape[] shapes = {shape1, shape2, shape3};
// Dynamic dispatch works with interfaces too!
System.out.println("\n=== CALCULATING AREAS AND PERIMETERS ===");
for (Shape shape : shapes) {
shape.displayInfo(); // Runtime decision!
System.out.println("---");
}
// Process shapes polymorphically
System.out.println("=== BATCH PROCESSING ===");
processShapes(shapes);
// Accessing implementation-specific methods
System.out.println("\n=== SHAPE-SPECIFIC OPERATIONS ===");
for (Shape shape : shapes) {
if (shape instanceof Circle) {
Circle circle = (Circle) shape;
circle.drawCircle();
} else if (shape instanceof Rectangle) {
Rectangle rect = (Rectangle) shape;
System.out.println("Is square? " + rect.isSquare());
} else if (shape instanceof Triangle) {
Triangle triangle = (Triangle) shape;
System.out.println("Triangle type: " + triangle.getTriangleType());
}
}
}
// Method that works with ANY Shape implementation
public static void processShapes(Shape[] shapes) {
double totalArea = 0;
double totalPerimeter = 0;
for (Shape shape : shapes) {
totalArea += shape.calculateArea();    // Dynamic dispatch!
totalPerimeter += shape.calculatePerimeter(); // Dynamic dispatch!
}
System.out.println("Total Area of all shapes: " + totalArea);
System.out.println("Total Perimeter of all shapes: " + totalPerimeter);
System.out.println("Average Area: " + (totalArea / shapes.length));
System.out.println("Average Perimeter: " + (totalPerimeter / shapes.length));
}
}

Output:

=== DYNAMIC DISPATCH WITH INTERFACES ===
=== CALCULATING AREAS AND PERIMETERS ===
Circle with radius: 5.0
Area: 78.53981633974483
Perimeter: 31.41592653589793
---
Rectangle: 4.0 x 6.0
Area: 24.0
Perimeter: 20.0
---
Triangle with sides: 3.0, 4.0, 5.0
Area: 6.0
Perimeter: 12.0
---
=== BATCH PROCESSING ===
Total Area of all shapes: 108.53981633974483
Total Perimeter of all shapes: 63.41592653589793
Average Area: 36.17993877991494
Average Perimeter: 21.138642178632643
=== SHAPE-SPECIFIC OPERATIONS ===
Drawing a perfect circle
Is square? false
Triangle type: Scalene

How Dynamic Method Dispatch Works

Compile-time vs Runtime

class Parent {
void show() { System.out.println("Parent's show"); }
}
class Child extends Parent {
@Override
void show() { System.out.println("Child's show"); }
}
public class DispatchMechanism {
public static void main(String[] args) {
Parent obj = new Child();  // Parent reference, Child object
// ✅ COMPILE-TIME: Reference type (Parent) determines which methods are available
// ✅ RUNTIME: Object type (Child) determines which implementation is called
obj.show();  // Output: "Child's show" - Dynamic Dispatch!
}
}

Virtual Method Table (VTable)

  • JVM maintains a virtual method table for each class
  • Contains pointers to actual method implementations
  • At runtime, JVM looks up the correct method in VTable
  • This enables runtime method resolution

Benefits of Dynamic Method Dispatch

✅ Advantages:

  • Flexibility - Code works with any subclass without modification
  • Extensibility - Easy to add new subclasses
  • Maintainability - Reduces code duplication
  • Runtime polymorphism - Enables powerful OOP patterns
  • Framework design - Essential for libraries and frameworks

✅ Real-World Applications:

  • GUI frameworks - Button clicks, event handlers
  • Plugin systems - Loading different implementations
  • Database drivers - Different database implementations
  • Game development - Different character behaviors
  • Payment gateways - Different payment methods

Common Pitfalls and Best Practices

class PitfallsDemo {
static class A {
void show() { System.out.println("A"); }
}
static class B extends A {
@Override
void show() { System.out.println("B"); }
void special() { System.out.println("B special"); }
}
public static void main(String[] args) {
A obj = new B();
// ✅ WORKS: Dynamic dispatch for overridden methods
obj.show();  // "B"
// ❌ DOESN'T WORK: Reference type limits available methods
// obj.special();  // Compilation error!
// ✅ SOLUTION: Explicit casting
if (obj instanceof B) {
B bObj = (B) obj;
bObj.special();  // Works!
}
}
}

Best Practices:

  1. Use abstract classes/interfaces for base types
  2. Design for extension - make methods protected when appropriate
  3. Use instanceof sparingly - often indicates design issues
  4. Favor composition when inheritance doesn't make sense
  5. Document overriding methods properly

Conclusion

Dynamic Method Dispatch is the magic behind Java's polymorphism:

  • Runtime method resolution - JVM decides which method to call
  • Based on actual object type - not reference type
  • Enables flexible, extensible code - works with future subclasses
  • Core of object-oriented programming - true polymorphism
  • Works with both classes and interfaces - universal mechanism

Key Takeaways:

  • Reference type determines which methods are available at compile time
  • Object type determines which implementation is called at runtime
  • Essential for framework design and extensible architectures
  • Enables "program to interface, not implementation" principle
  • Powerful but requires careful design to avoid pitfalls

Dynamic Method Dispatch is what makes Java truly object-oriented, allowing you to write flexible, maintainable code that can work with objects you haven't even created yet!

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper