Polymorphism

Polymorphism is a core concept in object-oriented programming (OOP) that allows methods to perform different tasks based on the object that invokes them. In Java, polymorphism enables you to define a single interface or method and have multiple implementations for it, depending on the object type.

Types of Polymorphism in Java

There are two main types of polymorphism in Java:

  1. Compile-Time Polymorphism (Static Binding)
  2. Run-Time Polymorphism (Dynamic Binding)

Polymorphism

Polymorphism allows methods to perform different functions based on the object that it is called on.

Compile-Time Polymorphism (Method Overloading)

This occurs when multiple methods have the same name but differ in their parameters.

Example:

class MathOperations {
// Method to add two integers
public int add(int a, int b) {
return a + b;
}

// Method to add two doubles
public double add(double a, double b) {
return a + b;
}
}

public class Main {
public static void main(String[] args) {
MathOperations mathOp = new MathOperations();
System.out.println(mathOp.add(3, 4)); // Output: 7 (integer addition)
System.out.println(mathOp.add(3.5, 4.5)); // Output: 8.0 (double addition)
}
}

Explanation:

  • The MathOperations class has two add methods: one for integers and one for doubles. The correct method is determined at compile time based on the arguments provided.

Run-Time Polymorphism (Method Overriding)

This occurs when a subclass provides a specific implementation of a method that is already defined in its superclass.

Example:

class Animal {
public void makeSound() {
System.out.println("Some sound");
}
}

class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Bark");
}
}

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

public class Main {
public static void main(String[] args) {
Animal myDog = new Dog();
Animal myCat = new Cat();
myDog.makeSound(); // Output: Bark
myCat.makeSound(); // Output: Meow
}
}

Explanation:

  • The Animal class has a method makeSound. The Dog and Cat classes override this method to provide their specific sounds.
  • When makeSound is called on myDog or myCat, the correct method is determined at runtime based on the object type.

Abstract Classes and Functions

An abstract class cannot be instantiated on its own and often contains abstract methods that subclasses must implement.

Example:

abstract class Shape {
// Abstract method for calculating area
public abstract double area();
}

class Circle extends Shape {
private double radius;

public Circle(double radius) {
this.radius = radius;
}

@Override
public double area() {
return Math.PI * radius * radius;
}
}

class Square extends Shape {
private double side;

public Square(double side) {
this.side = side;
}

@Override
public double area() {
return side * side;
}
}

public class Main {
public static void main(String[] args) {
Shape circle = new Circle(5);
Shape square = new Square(4);
System.out.println("Circle Area: " + circle.area()); // Output: Circle Area: 78.53981633974483
System.out.println("Square Area: " + square.area()); // Output: Square Area: 16.0
}
}

Explanation:

  • The Shape class is abstract and defines an abstract method area.
  • The Circle and Square classes implement the area method.
  • The main method creates instances of Circle and Square and calls their area methods.

Abstract Functions Practical Example

This example demonstrates how abstract methods enforce a contract for subclasses.

Example:

abstract class Vehicle {
// Abstract method
public abstract void move();
}

class Car extends Vehicle {
@Override
public void move() {
System.out.println("Driving on the road");
}
}

class Boat extends Vehicle {
@Override
public void move() {
System.out.println("Sailing on water");
}
}

public class Main {
public static void main(String[] args) {
Vehicle car = new Car();
Vehicle boat = new Boat();
car.move(); // Output: Driving on the road
boat.move(); // Output: Sailing on water
}
}

Explanation:

  • The Vehicle class defines an abstract method move.
  • The Car and Boat classes implement move to provide specific behaviors.
  • The main method creates instances of Car and Boat and calls their move methods.

Interfaces

An interface defines a contract with methods that classes must implement.

Example:

interface SoundMaker {
void makeSound();
}

class Dog implements SoundMaker {
@Override
public void makeSound() {
System.out.println("Bark");
}
}

class Cat implements SoundMaker {
@Override
public void makeSound() {
System.out.println("Meow");
}
}

public class Main {
public static void main(String[] args) {
SoundMaker dog = new Dog();
SoundMaker cat = new Cat();
dog.makeSound(); // Output: Bark
cat.makeSound(); // Output: Meow
}
}

Explanation:

  • The SoundMaker interface defines a method makeSound.
  • The Dog and Cat classes implement the makeSound method.
  • The main method creates instances of Dog and Cat and calls their makeSound methods.

Practical Uses of Interfaces

Interfaces allow different classes to implement the same methods, facilitating code organization and polymorphism.

Example:

public class Main {
public static void animalSound(SoundMaker animal) {
animal.makeSound();
}

public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
animalSound(dog); // Output: Bark
animalSound(cat); // Output: Meow
}
}

Explanation:

  • The animalSound method accepts any object that implements the SoundMaker interface.
  • This promotes loose coupling and flexibility in code design.

Interfaces Focused View

A focused view emphasizes how interfaces standardize method implementations across classes.

Example:

class Bird implements SoundMaker {
@Override
public void makeSound() {
System.out.println("Chirp");
}
}

public class Main {
public static void main(String[] args) {
SoundMaker bird = new Bird();
bird.makeSound(); // Output: Chirp
}
}

Explanation:

  • The Bird class implements the SoundMaker interface.
  • All classes that implement the interface can be treated the same way, regardless of their actual type.

Interfaces Different Implementation

Different classes can implement the same interface in distinct ways, showcasing polymorphism.

Example:

class Car implements SoundMaker {
@Override
public void makeSound() {
System.out.println("Vroom");
}
}

public class Main {
public static void main(String[] args) {
SoundMaker car = new Car();
car.makeSound(); // Output: Vroom
}
}

Explanation:

  • The Car class provides a different implementation of makeSound.
  • The car object demonstrates polymorphism by being treated as a SoundMaker.

Interfaces Unrelated Inheritance

Interfaces enable classes from different hierarchies to implement the same methods, allowing interchangeable usage.

Example:

class Truck implements SoundMaker {
@Override
public void makeSound() {
System.out.println("Honk");
}
}

public class Main {
public static void main(String[] args) {
SoundMaker truck = new Truck();
truck.makeSound(); // Output: Honk
}
}

Explanation:

  • The Truck class implements the SoundMaker interface.
  • Even though Truck is unrelated to Dog and Cat, it can be used in the same context, allowing for flexibility.

Summary

  1. Polymorphism enables methods to act differently based on the object type.
  2. Abstract Classes and Functions provide a template for subclasses to follow.
  3. Interfaces define contracts for classes to implement, allowing for uniform behavior across unrelated classes.