OOP Dispatch: Dynamic Binding, Single & Double Dispatch
Dispatch is the mechanism that selects which method implementation to execute when a method is called. This selection happens either statically at compile time or dynamically at runtime, and it forms the foundation of polymorphism.
What Is Dispatch?
Dispatch is the process of resolving a method call to a concrete implementation. How dispatch resolution works affects both the flexibility and performance of your software.
Types of Dispatch
- Static Dispatch: Resolution at compile time
- Dynamic Dispatch: Resolution at runtime
- Single Dispatch: Selection based on the receiver object’s type
- Double Dispatch: Selection based on two object types
- Multiple Dispatch: Selection based on multiple object types
Static vs Dynamic Dispatch
Static Dispatch (Overloading)
public class StaticDispatchDemo {
// Overloaded methods - static dispatch
public static class Printer {
public void print(Object obj) {
System.out.println("Printing Object: " + obj.toString());
}
public void print(String str) {
System.out.println("Printing String: " + str);
}
public void print(Integer num) {
System.out.println("Printing Integer: " + num);
}
public void print(String str, int count) {
for (int i = 0; i < count; i++) {
System.out.println("String " + (i + 1) + ": " + str);
}
}
}
public static void demonstrateStaticDispatch() {
Printer printer = new Printer();
// Direct calls - unambiguous selection
printer.print("Hello"); // print(String)
printer.print(42); // print(Integer)
printer.print("Test", 3); // print(String, int)
// Important case: static types decide!
Object obj1 = "Hello";
Object obj2 = 42;
printer.print(obj1); // print(Object) - compiler only knows Object type!
printer.print(obj2); // print(Object) - compiler only knows Object type!
// Cast to select the correct overload
printer.print((String) obj1); // print(String)
// printer.print((Integer) obj2); // ClassCastException at runtime!
}
}
Dynamic Dispatch (Overriding)
public class DynamicDispatchDemo {
// Base class with virtual methods
public static abstract class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
// Virtual method - dynamically bound
public String makeSound() {
return name + " makes a sound";
}
// Another virtual method
public String move() {
return name + " moves";
}
// Final method - cannot be overridden
public final String getName() {
return name;
}
// Abstract method - must be overridden
public abstract String getType();
}
// Concrete subclasses
public static class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public String makeSound() {
return name + " barks: Woof!";
}
@Override
public String move() {
return name + " runs happily";
}
@Override
public String getType() {
return "Dog";
}
// Additional method
public String wagTail() {
return name + " wags tail excitedly";
}
}
public static class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public String makeSound() {
return name + " meows: Meow!";
}
@Override
public String move() {
return name + " sneaks silently";
}
@Override
public String getType() {
return "Cat";
}
public String purr() {
return name + " purrs contentedly";
}
}
public static class Bird extends Animal {
public Bird(String name) {
super(name);
}
@Override
public String makeSound() {
return name + " chirps: Tweet!";
}
@Override
public String move() {
return name + " flies gracefully";
}
@Override
public String getType() {
return "Bird";
}
public String sing() {
return name + " sings a beautiful song";
}
}
// Demonstrating dynamic dispatch
public static void demonstrateDynamicDispatch() {
List<Animal> animals = new ArrayList<>();
animals.add(new Dog("Buddy"));
animals.add(new Cat("Whiskers"));
animals.add(new Bird("Tweety"));
System.out.println("=== Dynamic Dispatch ===");
for (Animal animal : animals) {
// Dynamic binding - runtime decides!
System.out.println(animal.getType() + ": " + animal.makeSound());
System.out.println(animal.getType() + ": " + animal.move());
System.out.println();
}
// Polymorphic processing
processAnimal(animals.get(0)); // Dog
processAnimal(animals.get(1)); // Cat
processAnimal(animals.get(2)); // Bird
}
// Polymorphic method
public static void processAnimal(Animal animal) {
System.out.println("Processing " + animal.getType());
// Dynamic dispatch
String sound = animal.makeSound();
String movement = animal.move();
System.out.println(" Sound: " + sound);
System.out.println(" Movement: " + movement);
// Type-specific operations (with instanceof)
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
System.out.println(" Special: " + dog.wagTail());
} else if (animal instanceof Cat) {
Cat cat = (Cat) animal;
System.out.println(" Special: " + cat.purr());
} else if (animal instanceof Bird) {
Bird bird = (Bird) animal;
System.out.println(" Special: " + bird.sing());
}
}
}
Single Dispatch
How Single Dispatch Works
public class SingleDispatchDemo {
// Single dispatch - only the receiver type matters
public interface Shape {
double area();
double perimeter();
String getType();
void accept(ShapeVisitor visitor);
}
public static class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
@Override
public double perimeter() {
return 2 * Math.PI * radius;
}
@Override
public String getType() {
return "Circle";
}
@Override
public void accept(ShapeVisitor visitor) {
visitor.visitCircle(this); // Single dispatch
}
public double getRadius() { return radius; }
}
public static class Rectangle implements Shape {
private double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
@Override
public double perimeter() {
return 2 * (width + height);
}
@Override
public String getType() {
return "Rectangle";
}
@Override
public void accept(ShapeVisitor visitor) {
visitor.visitRectangle(this); // Single dispatch
}
public double getWidth() { return width; }
public double getHeight() { return height; }
}
public static class Triangle implements Shape {
private double base, height;
public Triangle(double base, double height) {
this.base = base;
this.height = height;
}
@Override
public double area() {
return 0.5 * base * height;
}
@Override
public double perimeter() {
// Simplified: equilateral triangle
double side = base;
return 3 * side;
}
@Override
public String getType() {
return "Triangle";
}
@Override
public void accept(ShapeVisitor visitor) {
visitor.visitTriangle(this); // Single dispatch
}
public double getBase() { return base; }
public double getHeight() { return height; }
}
// Demonstration
public static void demonstrateSingleDispatch() {
List<Shape> shapes = new ArrayList<>();
shapes.add(new Circle(2.0));
shapes.add(new Rectangle(3.0, 4.0));
shapes.add(new Triangle(5.0, 3.0));
System.out.println("=== Single Dispatch ===");
for (Shape shape : shapes) {
System.out.println(shape.getType());
System.out.println(" Area: " + String.format("%.2f", shape.area()));
System.out.println(" Perimeter: " + String.format("%.2f", shape.perimeter()));
System.out.println();
}
}
}
Double Dispatch with the Visitor Pattern
Using the Visitor Pattern for Double Dispatch
public class DoubleDispatchDemo {
// Visitor Interface
public interface ShapeVisitor {
void visitCircle(Circle circle);
void visitRectangle(Rectangle rectangle);
void visitTriangle(Triangle triangle);
double getResult();
}
// Concrete visitor for area calculation
public static class AreaVisitor implements ShapeVisitor {
private double totalArea = 0.0;
@Override
public void visitCircle(Circle circle) {
double area = Math.PI * circle.getRadius() * circle.getRadius();
totalArea += area;
System.out.println("Circle area: " + String.format("%.2f", area));
}
@Override
public void visitRectangle(Rectangle rectangle) {
double area = rectangle.getWidth() * rectangle.getHeight();
totalArea += area;
System.out.println("Rectangle area: " + String.format("%.2f", area));
}
@Override
public void visitTriangle(Triangle triangle) {
double area = 0.5 * triangle.getBase() * triangle.getHeight();
totalArea += area;
System.out.println("Triangle area: " + String.format("%.2f", area));
}
@Override
public double getResult() {
return totalArea;
}
}
// Visitor for perimeter calculation
public static class PerimeterVisitor implements ShapeVisitor {
private double totalPerimeter = 0.0;
@Override
public void visitCircle(Circle circle) {
double perimeter = 2 * Math.PI * circle.getRadius();
totalPerimeter += perimeter;
System.out.println("Circle perimeter: " + String.format("%.2f", perimeter));
}
@Override
public void visitRectangle(Rectangle rectangle) {
double perimeter = 2 * (rectangle.getWidth() + rectangle.getHeight());
totalPerimeter += perimeter;
System.out.println("Rectangle perimeter: " + String.format("%.2f", perimeter));
}
@Override
public void visitTriangle(Triangle triangle) {
double perimeter = 3 * triangle.getBase(); // Simplified
totalPerimeter += perimeter;
System.out.println("Triangle perimeter: " + String.format("%.2f", perimeter));
}
@Override
public double getResult() {
return totalPerimeter;
}
}
// Visitor for XML export
public static class XMLExportVisitor implements ShapeVisitor {
private StringBuilder xml = new StringBuilder();
@Override
public void visitCircle(Circle circle) {
xml.append("<shape type=\"circle\">\n");
xml.append(" <radius>").append(circle.getRadius()).append("</radius>\n");
xml.append(" <area>").append(String.format("%.2f", Math.PI * circle.getRadius() * circle.getRadius())).append("</area>\n");
xml.append("</shape>\n");
}
@Override
public void visitRectangle(Rectangle rectangle) {
xml.append("<shape type=\"rectangle\">\n");
xml.append(" <width>").append(rectangle.getWidth()).append("</width>\n");
xml.append(" <height>").append(rectangle.getHeight()).append("</height>\n");
xml.append(" <area>").append(String.format("%.2f", rectangle.getWidth() * rectangle.getHeight())).append("</area>\n");
xml.append("</shape>\n");
}
@Override
public void visitTriangle(Triangle triangle) {
xml.append("<shape type=\"triangle\">\n");
xml.append(" <base>").append(triangle.getBase()).append("</base>\n");
xml.append(" <height>").append(triangle.getHeight()).append("</height>\n");
xml.append(" <area>").append(String.format("%.2f", 0.5 * triangle.getBase() * triangle.getHeight())).append("</area>\n");
xml.append("</shape>\n");
}
@Override
public double getResult() {
return 0; // Not relevant for this visitor
}
public String getXML() {
return "<shapes>\n" + xml.toString() + "</shapes>";
}
}
// Demonstrating double dispatch
public static void demonstrateDoubleDispatch() {
List<Shape> shapes = new ArrayList<>();
shapes.add(new Circle(2.0));
shapes.add(new Rectangle(3.0, 4.0));
shapes.add(new Triangle(5.0, 3.0));
System.out.println("=== Double Dispatch with Visitor Pattern ===");
// Area visitor
System.out.println("Area calculation:");
AreaVisitor areaVisitor = new AreaVisitor();
for (Shape shape : shapes) {
shape.accept(areaVisitor); // Double dispatch!
}
System.out.println("Total area: " + String.format("%.2f", areaVisitor.getResult()));
System.out.println();
// Perimeter visitor
System.out.println("Perimeter calculation:");
PerimeterVisitor perimeterVisitor = new PerimeterVisitor();
for (Shape shape : shapes) {
shape.accept(perimeterVisitor); // Double dispatch!
}
System.out.println("Total perimeter: " + String.format("%.2f", perimeterVisitor.getResult()));
System.out.println();
// XML export visitor
System.out.println("XML export:");
XMLExportVisitor xmlVisitor = new XMLExportVisitor();
for (Shape shape : shapes) {
shape.accept(xmlVisitor); // Double dispatch!
}
System.out.println(xmlVisitor.getXML());
}
}
Multiple Dispatch
Simulating Multiple Dispatch
public class MultipleDispatchDemo {
// Problem: operations that depend on two types
public interface Collider {
void collideWith(Collider other);
String getType();
}
public static class Asteroid implements Collider {
private String name;
private double mass;
public Asteroid(String name, double mass) {
this.name = name;
this.mass = mass;
}
@Override
public void collideWith(Collider other) {
// Single dispatch - only the other type decides
System.out.println(name + " collides with " + other.getType());
}
@Override
public String getType() {
return "Asteroid";
}
public double getMass() { return mass; }
public String getName() { return name; }
}
public static class Spaceship implements Collider {
private String name;
private double shield;
public Spaceship(String name, double shield) {
this.name = name;
this.shield = shield;
}
@Override
public void collideWith(Collider other) {
// Single dispatch - problem: we don't know which type we're colliding with
System.out.println(name + " collides with " + other.getType());
}
@Override
public String getType() {
return "Spaceship";
}
public double getShield() { return shield; }
public String getName() { return name; }
}
public static class SpaceStation implements Collider {
private String name;
private double hull;
public SpaceStation(String name, double hull) {
this.name = name;
this.hull = hull;
}
@Override
public void collideWith(Collider other) {
System.out.println(name + " collides with " + other.getType());
}
@Override
public String getType() {
return "SpaceStation";
}
public double getHull() { return hull; }
public String getName() { return name; }
}
// Solution using the double dispatch pattern
public interface CollisionHandler {
void handleAsteroidAsteroid(Asteroid a1, Asteroid a2);
void handleAsteroidSpaceship(Asteroid asteroid, Spaceship spaceship);
void handleAsteroidStation(Asteroid asteroid, SpaceStation station);
void handleSpaceshipSpaceship(Spaceship s1, Spaceship s2);
void handleSpaceshipStation(Spaceship spaceship, SpaceStation station);
void handleStationStation(SpaceStation s1, SpaceStation s2);
}
public static class DefaultCollisionHandler implements CollisionHandler {
@Override
public void handleAsteroidAsteroid(Asteroid a1, Asteroid a2) {
double totalMass = a1.getMass() + a2.getMass();
System.out.println("Asteroid collision: " + a1.getName() + " + " + a2.getName() +
" (total mass: " + String.format("%.1f", totalMass) + ")");
}
@Override
public void handleAsteroidSpaceship(Asteroid asteroid, Spaceship spaceship) {
double damage = asteroid.getMass() * 0.1;
spaceship.setShield(spaceship.getShield() - damage);
System.out.println("Asteroid-Spaceship collision: " + asteroid.getName() +
" hits " + spaceship.getName() + " (damage: " +
String.format("%.1f", damage) + ")");
}
@Override
public void handleAsteroidStation(Asteroid asteroid, SpaceStation station) {
double damage = asteroid.getMass() * 0.05;
station.setHull(station.getHull() - damage);
System.out.println("Asteroid-Station collision: " + asteroid.getName() +
" hits " + station.getName() + " (damage: " +
String.format("%.1f", damage) + ")");
}
@Override
public void handleSpaceshipSpaceship(Spaceship s1, Spaceship s2) {
System.out.println("Spaceship collision: " + s1.getName() + " + " + s2.getName());
}
@Override
public void handleSpaceshipStation(Spaceship spaceship, SpaceStation station) {
System.out.println("Spaceship-Station collision: " + spaceship.getName() +
" docks at " + station.getName());
}
@Override
public void handleStationStation(SpaceStation s1, SpaceStation s2) {
System.out.println("Station collision: " + s1.getName() + " + " + s2.getName());
}
}
// Enhanced colliders with double dispatch
public static class ImprovedAsteroid extends Asteroid {
public ImprovedAsteroid(String name, double mass) {
super(name, mass);
}
@Override
public void collideWith(Collider other) {
other.collideWithAsteroid(this); // Reversal for double dispatch
}
public void collideWithAsteroid(Asteroid other) {
CollisionHandler handler = new DefaultCollisionHandler();
handler.handleAsteroidAsteroid(this, other);
}
public void collideWithSpaceship(Spaceship spaceship) {
CollisionHandler handler = new DefaultCollisionHandler();
handler.handleAsteroidSpaceship(this, spaceship);
}
public void collideWithStation(SpaceStation station) {
CollisionHandler handler = new DefaultCollisionHandler();
handler.handleAsteroidStation(this, station);
}
}
public static class ImprovedSpaceship extends Spaceship {
public ImprovedSpaceship(String name, double shield) {
super(name, shield);
}
@Override
public void collideWith(Collider other) {
other.collideWithSpaceship(this);
}
public void collideWithAsteroid(Asteroid asteroid) {
CollisionHandler handler = new DefaultCollisionHandler();
handler.handleAsteroidSpaceship(asteroid, this);
}
public void collideWithSpaceship(Spaceship other) {
CollisionHandler handler = new DefaultCollisionHandler();
handler.handleSpaceshipSpaceship(this, other);
}
public void collideWithStation(SpaceStation station) {
CollisionHandler handler = new DefaultCollisionHandler();
handler.handleSpaceshipStation(this, station);
}
}
// Demonstration
public static void demonstrateMultipleDispatch() {
System.out.println("=== Multiple Dispatch Simulation ===");
// Simple single dispatch (the problem)
System.out.println("Single Dispatch (Problem):");
Collider asteroid1 = new Asteroid("Rocky", 100.0);
Collider spaceship1 = new Spaceship("Explorer", 50.0);
asteroid1.collideWith(spaceship1); // Only the type of spaceship1 is known
System.out.println();
// Double dispatch solution
System.out.println("Double Dispatch Solution:");
ImprovedAsteroid asteroid2 = new ImprovedAsteroid("Rocky", 100.0);
ImprovedSpaceship spaceship2 = new ImprovedSpaceship("Explorer", 50.0);
asteroid2.collideWith(spaceship2); // Both types are known!
System.out.println();
// More complex collisions
List<Collider> objects = Arrays.asList(
new ImprovedAsteroid("Rocky", 100.0),
new ImprovedSpaceship("Explorer", 50.0),
new ImprovedSpaceship("Voyager", 60.0),
new SpaceStation("Alpha", 200.0)
);
System.out.println("Complex collisions:");
for (int i = 0; i < objects.size(); i++) {
for (int j = i + 1; j < objects.size(); j++) {
objects.get(i).collideWith(objects.get(j));
}
}
}
}
Virtual Method Tables (vtable)
How vtables work
public class VTableDemo {
// Simplified representation of a vtable
public static class VTableVisualization {
// Base class
public static class Base {
// Virtual methods
public virtual void method1() { System.out.println("Base.method1"); }
public virtual void method2() { System.out.println("Base.method2"); }
// Concrete method
public void concreteMethod() { System.out.println("Base.concreteMethod"); }
}
// Derived class 1
public static class Derived1 extends Base {
@Override
public void method1() { System.out.println("Derived1.method1"); }
// method2 inherited
@Override
public void method2() { System.out.println("Derived1.method2"); }
}
// Derived class 2
public static class Derived2 extends Base {
// method1 inherited
@Override
public void method2() { System.out.println("Derived2.method2"); }
public void method3() { System.out.println("Derived2.method3"); }
}
// Demonstrating the vtable
public static void demonstrateVTable() {
System.out.println("=== VTable Demonstration ===");
Base base = new Base();
Base derived1 = new Derived1();
Base derived2 = new Derived2();
// Vtable for Base:
// [Base.method1, Base.method2]
System.out.println("Base object:");
base.method1(); // Base.method1
base.method2(); // Base.method2
// Vtable for Derived1:
// [Derived1.method1, Derived1.method2]
System.out.println("\nDerived1 object:");
derived1.method1(); // Derived1.method1
derived1.method2(); // Derived1.method2
// Vtable for Derived2:
// [Base.method1, Derived2.method2]
System.out.println("\nDerived2 object:");
derived2.method1(); // Base.method1 (inherited)
derived2.method2(); // Derived2.method2
// Concrete method - not in vtable
System.out.println("\nConcrete method:");
base.concreteMethod();
derived1.concreteMethod();
derived2.concreteMethod();
}
}
// Performance comparison
public static class PerformanceComparison {
// Final method - no virtual call
public static class FinalClass {
public final void finalMethod() {
// No vtable lookup
}
}
// Virtual method - vtable lookup
public static class VirtualClass {
public void virtualMethod() {
// vtable lookup required
}
}
public static void performanceTest() {
FinalClass finalObj = new FinalClass();
VirtualClass virtualObj = new VirtualClass();
int iterations = 100_000_000;
// Test final method
long start = System.nanoTime();
for (int i = 0; i < iterations; i++) {
finalObj.finalMethod();
}
long finalTime = System.nanoTime() - start;
// Test virtual method
start = System.nanoTime();
for (int i = 0; i < iterations; i++) {
virtualObj.virtualMethod();
}
long virtualTime = System.nanoTime() - start;
System.out.println("Performance Comparison:");
System.out.println("Final method: " + (finalTime / 1_000_000) + " ms");
System.out.println("Virtual method: " + (virtualTime / 1_000_000) + " ms");
System.out.println("Difference: " + ((virtualTime - finalTime) / 1_000_000) + " ms");
}
}
}
Dispatch in practice
Framework example: Plugin system
public class PluginDispatchDemo {
// Plugin interface
public interface Plugin {
void initialize();
void execute();
void shutdown();
String getName();
String getVersion();
}
// Abstract base class for plugins
public abstract class AbstractPlugin implements Plugin {
protected String name;
protected String version;
public AbstractPlugin(String name, String version) {
this.name = name;
this.version = version;
}
@Override
public final String getName() {
return name;
}
@Override
public final String getVersion() {
return version;
}
// Template Method Pattern
@Override
public final void execute() {
System.out.println("Executing plugin: " + name);
try {
doExecute();
System.out.println("Plugin execution completed: " + name);
} catch (Exception e) {
System.err.println("Plugin execution failed: " + name + " - " + e.getMessage());
}
}
// Abstract method for concrete implementation
protected abstract void doExecute();
// Default implementations
@Override
public void initialize() {
System.out.println("Initializing plugin: " + name);
}
@Override
public void shutdown() {
System.out.println("Shutting down plugin: " + name);
}
}
// Concrete plugins
public static class DatabasePlugin extends AbstractPlugin {
public DatabasePlugin() {
super("DatabasePlugin", "1.0.0");
}
@Override
protected void doExecute() {
System.out.println(" Connecting to database...");
System.out.println(" Executing database operations...");
System.out.println(" Closing database connection...");
}
@Override
public void initialize() {
super.initialize();
System.out.println(" Loading database driver...");
}
}
public static class LoggingPlugin extends AbstractPlugin {
public LoggingPlugin() {
super("LoggingPlugin", "2.1.0");
}
@Override
protected void doExecute() {
System.out.println(" Setting up loggers...");
System.out.println(" Configuring log levels...");
System.out.println(" Starting log rotation...");
}
@Override
public void shutdown() {
System.out.println(" Flushing log buffers...");
super.shutdown();
}
}
public static class SecurityPlugin extends AbstractPlugin {
public SecurityPlugin() {
super("SecurityPlugin", "1.5.2");
}
@Override
protected void doExecute() {
System.out.println(" Initializing security manager...");
System.out.println(" Setting up authentication...");
System.out.println(" Configuring authorization rules...");
}
}
// Plugin manager with dispatch
public static class PluginManager {
private List<Plugin> plugins = new ArrayList<>();
public void registerPlugin(Plugin plugin) {
plugins.add(plugin);
plugin.initialize();
}
public void executeAllPlugins() {
System.out.println("=== Executing all plugins ===");
for (Plugin plugin : plugins) {
plugin.execute(); // Dynamic dispatch
}
}
public void shutdownAllPlugins() {
System.out.println("\n=== Shutting down all plugins ===");
for (Plugin plugin : plugins) {
plugin.shutdown(); // Dynamic dispatch
}
}
public void executePluginByName(String name) {
for (Plugin plugin : plugins) {
if (plugin.getName().equals(name)) {
plugin.execute(); // Dynamic dispatch
return;
}
}
System.out.println("Plugin not found: " + name);
}
public void listPlugins() {
System.out.println("=== Registered Plugins ===");
for (Plugin plugin : plugins) {
System.out.println(plugin.getName() + " v" + plugin.getVersion());
}
}
}
// Demonstration
public static void demonstratePluginSystem() {
PluginManager manager = new PluginManager();
// Register plugins
manager.registerPlugin(new DatabasePlugin());
manager.registerPlugin(new LoggingPlugin());
manager.registerPlugin(new SecurityPlugin());
// Execute all plugins
manager.listPlugins();
manager.executeAllPlugins();
// Execute specific plugin
System.out.println("\n=== Executing specific plugin ===");
manager.executePluginByName("LoggingPlugin");
// Clean up
manager.shutdownAllPlugins();
}
}
Best Practices for Dispatch
1. Final for Performance
public class DispatchBestPractices {
// Final methods for performance
public static class PerformanceOptimized {
// Final method - no virtual call overhead
public final void criticalMethod() {
// Hot path code
}
// Final class - inheritance not allowed
public static final class ImmutableData {
private final int value;
public ImmutableData(int value) {
this.value = value;
}
public final int getValue() {
return value;
}
}
}
// Interface for flexibility
public interface Processor {
void process();
}
// Concrete implementation
public static class FastProcessor implements Processor {
@Override
public void process() {
// Implementation
}
}
}
2. Avoiding Dispatch Issues
public class DispatchProblems {
// Problem: overloading with reference types
public static class ProblematicOverloading {
public void process(Object obj) {
System.out.println("Processing Object");
}
public void process(String str) {
System.out.println("Processing String");
}
public void demonstrateProblem() {
Object obj = "Hello";
process(obj); // Calls process(Object)!
}
}
// Solution: explicit method names
public static class Solution {
public void processObject(Object obj) {
System.out.println("Processing Object");
}
public void processString(String str) {
System.out.println("Processing String");
}
public void demonstrateSolution() {
Object obj = "Hello";
if (obj instanceof String) {
processString((String) obj);
} else {
processObject(obj);
}
}
}
}
Key Concepts for Exams
Important Distinctions
-
Static vs Dynamic
- Static: Compile-time (overloading)
- Dynamic: Runtime (overriding)
-
Single vs Double Dispatch
- Single: One object type decides
- Double: Two object types decide
-
vtable vs Early Binding
- vtable: Virtual methods
- Early Binding: Final methods
Typical Exam Questions
- Explain Single vs Double Dispatch
- Implement the Visitor Pattern
- Compare static and dynamic binding
- Describe how vtables work
- Solve dispatch problems
Summary
Dispatch is fundamental to object-oriented programming:
- Static Dispatch: Predictable, fast (overloading)
- Dynamic Dispatch: Flexible, extensible (overriding)
- Single Dispatch: Standard in most OOP languages
- Double Dispatch: Achievable via the Visitor Pattern
- vtables: Efficient implementation of dynamic dispatch
Applying dispatch correctly enables flexible, maintainable, and performant software architectures.
Continue Your OOP Learning Path
All OOP articles are now complete. Return to the first article: Objektorientierte Programmierung OOP Grundlagen.

