OOP

Java Dynamic Binding Tutorial

Dynamic Binding (also known as late binding or runtime polymorphism) refers to the process in which the method to be executed is determined at runtime rather than at compile time.

In Java, dynamic binding happens when you use method overriding, allowing a subclass to provide a specific implementation of a method that is already defined in its superclass.

Java uses dynamic binding to resolve overridden methods during runtime, enabling powerful polymorphic behavior. This feature plays a crucial role in achieving runtime polymorphism.

In this tutorial, we will explore dynamic binding, how it works with inheritance and polymorphism, and demonstrate it through multiple examples.

1. What is Dynamic Binding in Java?

Dynamic binding is the process by which the Java Virtual Machine (JVM) determines at runtime which version of an overridden method to call. This is different from static binding (or early binding), where the method call is resolved at compile time.

Key Points:

In dynamic binding, the method call is resolved at runtime based on the actual object (not the reference type) that invokes the method.
Only instance methods (non-static) are resolved through dynamic binding.
Dynamic binding enables runtime polymorphism where a superclass reference variable can refer to a subclass object and invoke the subclass's overridden method.

2. Static vs Dynamic Binding

Static Binding: The method call is resolved at compile time. It occurs in cases of method overloading, static methods, and private methods.
Dynamic Binding: The method call is resolved at runtime, based on the object being referred to. This happens with method overriding.

Example of Static Binding:

public class StaticBindingExample {
    public static void printMessage() {
        System.out.println("Static binding: Compile-time resolution");
    }

    public static void main(String[] args) {
        StaticBindingExample.printMessage();  // Resolved at compile-time
    }
}

Explanation:

The method printMessage() is static, so it is resolved at compile time (static binding).

Output:

Static binding: Compile-time resolution

3. Dynamic Binding with Method Overriding

Dynamic binding occurs with method overriding. When a method in a subclass overrides a method in the superclass, the decision about which method to execute is made at runtime based on the actual object.

Example of Dynamic Binding with Overriding:

class Animal {
    // Overridden method
    public void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    // Overriding method
    @Override
    public void sound() {
        System.out.println("Dog barks");
    }
}

public class DynamicBindingExample {
    public static void main(String[] args) {
        Animal myAnimal = new Animal();  // Animal reference, Animal object
        Animal myDog = new Dog();        // Animal reference, Dog object

        // Method call determined by the object type at runtime (Dynamic binding)
        myAnimal.sound();  // 
        Animal makes a sound
        myDog.sound();   		// 
        Dog barks
    }
}

Explanation:

The sound() method is overridden in the Dog class.
When myDog.sound() is called, it dynamically resolves to the Dog class's implementation, even though the reference type is Animal.

Output:

Animal makes a sound
Dog barks

4. Dynamic Binding with Polymorphism

Polymorphism allows one interface to be used for a general class of actions. In the context of dynamic binding, polymorphism refers to the ability of a superclass reference to refer to objects of subclass types, enabling dynamic method dispatch.

Example of Dynamic Binding with Polymorphism:

class Shape {
    public void draw() {
        System.out.println("Drawing a generic shape");
    }
}

class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }
}

class Square extends Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a square");
    }
}

public class PolymorphismExample {
    public static void main(String[] args) {
        // Superclass reference pointing to different subclass objects
        Shape shape1 = new Circle();
        Shape shape2 = new Square();

        // Runtime polymorphism (Dynamic binding)
        shape1.draw();  // 
        Drawing a circle
        shape2.draw();  // 
        Drawing a square
    }
}

Explanation:

The draw() method is overridden in both Circle and Square subclasses.
The type of object (Circle or Square) is resolved at runtime, and the appropriate draw() method is called, demonstrating dynamic binding and polymorphism.

Output:

Drawing a circle
Drawing a square

5. Dynamic Binding and Casting

Sometimes, a superclass reference may be cast to a subclass reference. Casting does not change the actual object, so dynamic binding still occurs based on the actual object type.

Example of Dynamic Binding with Casting:

class Vehicle {
    public void start() {
        System.out.println("Starting a vehicle");
    }
}

class Car extends Vehicle {
    @Override
    public void start() {
        System.out.println("Starting a car");
    }

    public void accelerate() {
        System.out.println("Car is accelerating");
    }
}

public class CastingExample {
    public static void main(String[] args) {
        Vehicle vehicle = new Car();  // Vehicle reference, Car object

        // Dynamic binding for overridden method
        vehicle.start();  // 
        Starting a car

        // Downcasting to access subclass-specific method
        Car car = (Car) vehicle;
        car.accelerate();  // 
        Car is accelerating
    }
}

Explanation:

The start() method is overridden in Car, and dynamic binding occurs when the vehicle.start() method is called.
The reference vehicle is cast to Car, allowing the use of subclass-specific methods like accelerate().

Output:

Starting a car
Car is accelerating

6. Dynamic Binding with Abstract Classes

Dynamic binding also works with abstract classes. When a method in an abstract class is implemented in a subclass, the method call is dynamically bound to the subclass implementation at runtime.

Example of Dynamic Binding with Abstract Classes:

abstract class Animal {
    // Abstract method
    public abstract void sound();
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("Cat meows");
    }
}

class Cow extends Animal {
    @Override
    public void sound() {
        System.out.println("Cow moos");
    }
}

public class AbstractDynamicBindingExample {
    public static void main(String[] args) {
        Animal cat = new Cat();  // Animal reference, Cat object
        Animal cow = new Cow();  // Animal reference, Cow object

        // Dynamic binding for abstract method
        cat.sound();  // 
        Cat meows
        cow.sound();  // 
        Cow moos
    }
}

Explanation:

The sound() method is abstract in the Animal class.
The actual method call is determined at runtime based on the type of object (Cat or Cow), demonstrating dynamic binding with abstract classes.

Output:

Cat meows
Cow moos

7. Dynamic Binding with Interfaces

Dynamic binding works similarly with interfaces. When a method in an interface is implemented by a class, dynamic binding determines which implementation to call based on the actual object type.

Example of Dynamic Binding with Interfaces:

interface Payment {
    void pay();
}

class CreditCardPayment implements Payment {
    @Override
    public void pay() {
        System.out.println("Paying with credit card");
    }
}

class PaypalPayment implements Payment {
    @Override
    public void pay() {
        System.out.println("Paying with PayPal");
    }
}

public class InterfaceDynamicBindingExample {
    public static void main(String[] args) {
        Payment payment1 = new CreditCardPayment();  // Payment reference, CreditCardPayment object
        Payment payment2 = new PaypalPayment();      // Payment reference, PaypalPayment object

        // Dynamic binding for interface method
        payment1.pay();  // 
        Paying with credit card
        payment2.pay();  // 
        Paying with PayPal
    }
}

Explanation:

The Payment interface defines the pay() method.
The actual method implementation is determined at runtime based on the type of object (CreditCardPayment or PaypalPayment), demonstrating dynamic binding with interfaces.

Output:

Paying with credit card
Paying with PayPal

8. Dynamic Binding vs. Static Binding

While static binding (or early binding) occurs at compile time, dynamic binding happens at runtime. Methods like static methods, final methods, and private methods are resolved using static binding.

Example:

Static vs. Dynamic Binding

class StaticBindingExample {
    // Static method (Static Binding)
    public static void staticMethod() {
        System.out.println("Static binding: Compile-time resolution");
    }

    // Non-static method (Dynamic Binding)
    public void dynamicMethod() {
        System.out.println("Dynamic binding: Runtime resolution");
    }
}

public class BindingComparisonExample {
    public static void main(String[] args) {
        StaticBindingExample example = new StaticBindingExample();

        // Static binding (resolved at compile-time)
        example.staticMethod();

        // Dynamic binding (resolved at runtime)
        example.dynamicMethod();
    }
}

Explanation:

The staticMethod() is statically bound and resolved at compile time.
The dynamicMethod() is dynamically bound and resolved at runtime.

Output:

Static binding: Compile-time resolution
Dynamic binding: Runtime resolution

Conclusion

Dynamic Binding is a powerful concept in Java that allows for flexibility and reusability through runtime polymorphism. With dynamic binding, the JVM decides at runtime which method to execute based on the actual object type, not the reference type.

In this tutorial, we covered:

Static vs. Dynamic Binding.
Dynamic binding with method overriding.
How dynamic binding enables polymorphism.
How to use dynamic binding with casting, abstract classes, and interfaces.

By understanding dynamic binding, you can write flexible and extensible Java applications that leverage polymorphism and method overriding for more dynamic and runtime-specific behaviors.

Related posts

Java Hidden Classes

Java sealed classes

Java Static Classes