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.