IT234 Object Oriented Programming with Java

Object Oriented Programming with JavaUnit 119 min read

Polymorphism, Abstraction, Sealed Classes & Design Patterns

Unit 11 of Object Oriented Programming with Java explores polymorphism (compile-time vs. runtime), abstraction (interfaces vs. abstract classes), sealed hierarchies, and design patterns (Singleton, Factory, Observer) with real-world Java examples and implementation traces.

Key Concepts and Definitions

1. Polymorphism

Polymorphism allows objects of different classes to be treated as objects of a common superclass. It is divided into two types:

add(int a, int b)add(double a, double b)MathUtils
Method overloading: same name, different parameter types
Animal0Dog1Cat2
Polymorphic array: Animal[] animals = {new Dog(), new Cat(), new Animal()}; (Dog and Cat objects treated as Animal)
AnimalDogCat
Runtime Polymorphism: Animal superclass with Dog and Cat subclasses overriding sound()

1.1 Compile-Time Polymorphism (Method Overloading)

  • Same method name, different parameters (type, number, or order).
  • Resolved during compilation.
  • Example: add(int a, int b) and add(double a, double b).

1.2 Runtime Polymorphism (Method Overriding)

  • Same method signature in superclass and subclass.
  • Resolved during runtime using dynamic method dispatch.
  • Example: Animal class with sound() overridden in Dog and Cat.
class Animal {
    void sound() { System.out.println("Animal sound"); }
}
class Dog extends Animal {
    @Override
    void sound() { System.out.println("Bark"); }
}
class Cat extends Animal {
    @Override
    void sound() { System.out.println("Meow"); }
}

Trace:

Step Object Type Method Called
1 Animal a = new Dog() Dog.sound()
2 Animal c = new Cat() Cat.sound()

2. Abstraction

Abstraction hides implementation details and shows only essential features.

classDiagram
    class Shape {
        <<abstract>>
        +area() double
    }
    class Circle {
        -radius double
        +area() double
    }
    class Rectangle {
        -length double
        -width double
        +area() double
    }
    Shape <|-- Circle
    Shape <|-- Rectangle
Abstract Shape hierarchy with concrete implementations

2.1 Abstract Classes

  • Cannot be instantiated.
  • Can have abstract and concrete methods.
  • Use abstract keyword.
  • Example: Shape with abstract area() method.
abstract class Shape {
    abstract double area();
}
class Circle extends Shape {
    double radius;
    Circle(double r) { radius = r; }
    @Override
    double area() { return Math.PI * radius * radius; }
}

2.2 Interfaces

  • Fully abstract (all methods are public abstract by default).
  • Can have default and static methods (Java 8+).
  • A class can implement multiple interfaces.
  • Example: Drawable interface for shapes.
interface Drawable {
    void draw();
    default void display() { System.out.println("Drawing..."); }
}
class Rectangle implements Drawable {
    @Override
    public void draw() { System.out.println("Drawing Rectangle"); }
}

Comparison Table:

Feature Abstract Class Interface
Instantiation No No
Abstract Methods Yes Yes (default in Java 8+)
Concrete Methods Yes Yes (default/static)
Fields Yes (non-final) No (only public static final)
Multiple Inheritance No Yes (multiple interfaces)
extends/implements extends implements

3. Sealed Classes (Java 17+)

  • Restrict which classes can extend or implement them.
  • Improves type safety and documentation.
  • Example: Sealed hierarchy for PaymentMethod.
sealed interface PaymentMethod permits CreditCard, DebitCard, UPI {
    void processPayment(double amount);
}
final class CreditCard implements PaymentMethod {
    @Override
    public void processPayment(double amount) { /* ... */ }
}
non-sealed class DebitCard implements PaymentMethod {
    @Override
    public void processPayment(double amount) { /* ... */ }
}

Visualization:

classDiagram
    class PaymentMethod {
        <<sealed>>
        +processPayment(amount: double)
        +finalize() void
    }
    class CreditCard {
        +processPayment(amount: double)
    }
    class DebitCard {
        +processPayment(amount: double)
    }
    class UPI {
        +processPayment(amount: double)
    }
    PaymentMethod <|-- CreditCard
    PaymentMethod <|-- DebitCard
    PaymentMethod <|-- UPI
    note for PaymentMethod "sealed permits only CreditCard, DebitCard, UPI"

4. Design Patterns

Design patterns are reusable solutions to common problems.

VehicleFactoryCarBike
Factory Pattern: VehicleFactory creates Car/Bike objects via getVehicle()
NewsAgencyJournalist1Journalist2
Observer Pattern: NewsAgency notifies multiple Journalists

4.1 Singleton Pattern

  • Ensures a class has only one instance.
  • Used in logging, database connections.
  • Example: DatabaseConnection.
startgetInstance()getInstance()SingletonInstanceCreated
Singleton lifecycle: only one instance ever created
class DatabaseConnection {
    private static DatabaseConnection instance;
    private DatabaseConnection() {}
    public static DatabaseConnection getInstance() {
        if (instance == null) {
            instance = new DatabaseConnection();
        }
        return instance;
    }
}

Thread-Safe Singleton (Double-Checked Locking):

public static DatabaseConnection getInstance() {
    if (instance == null) {
        synchronized (DatabaseConnection.class) {
            if (instance == null) {
                instance = new DatabaseConnection();
            }
        }
    }
    return instance;
}

4.2 Factory Pattern

  • Creates objects without specifying the exact class.
  • Example: VehicleFactory for Car and Bike.
interface Vehicle {
    void drive();
}
class Car implements Vehicle { public void drive() { System.out.println("Driving Car"); } }
class Bike implements Vehicle { public void drive() { System.out.println("Riding Bike"); } }

class VehicleFactory {
    public Vehicle getVehicle(String type) {
        if (type.equals("car")) return new Car();
        else if (type.equals("bike")) return new Bike();
        throw new IllegalArgumentException("Invalid vehicle type");
    }
}

4.3 Observer Pattern

  • Notifies observers when an event occurs.
  • Example: NewsAgency and Journalists.
interface Observer {
    void update(String news);
}
class NewsAgency {
    private List<Observer> observers = new ArrayList<>();
    public void addObserver(Observer o) { observers.add(o); }
    public void notifyObservers(String news) {
        for (Observer o : observers) o.update(news);
    }
}
class Journalist implements Observer {
    @Override
    public void update(String news) { System.out.println("Journalist received: " + news); }
}

In the Real World

  1. Khalti (Nepal) uses the Singleton Pattern for its PaymentGateway class to ensure only one instance manages transactions, preventing duplicate payments.
  2. Daraz (Nepal) employs the Factory Pattern in its order processing system to create different order types (PhysicalOrder, DigitalOrder) dynamically.
  3. Ncell (Nepal) leverages Observer Pattern to notify users about promotions or balance updates via SMS/email.

Worked Example:

  • Bank Loan Interest Calculation (Polymorphism): Suppose a bank has Loan (abstract) with calculateInterest(), overridden in HomeLoan and CarLoan.
    abstract class Loan {
        abstract double calculateInterest(double principal, int years);
    }
    class HomeLoan extends Loan {
        @Override
        double calculateInterest(double p, int y) { return p * 0.08 * y; }
    }
    class CarLoan extends Loan {
        @Override
        double calculateInterest(double p, int y) { return p * 0.12 * y; }
    }
    
    Trace:
    Loan Type Principal (Rs.) Years Interest (Rs.)
    HomeLoan 5,000,000 10 400,000
    CarLoan 2,000,000 5 1,200,000

Exam Tip

  • Polymorphism: Focus on method overriding (runtime) vs. overloading (compile-time). Always trace object types and method calls.
  • Abstraction: Differentiate between abstract classes (partial abstraction) and interfaces (full abstraction). Remember default methods in interfaces.
  • Sealed Classes: Know the permits clause and how it restricts inheritance.
  • Design Patterns: Be ready to write code for Singleton, Factory, and Observer. Explain their use cases (e.g., Singleton for logging, Factory for object creation).
  • Common Pitfalls:
    • Forgetting @Override annotation.
    • Misusing static methods in interfaces.
    • Thread-safety issues in Singleton (use synchronized or enum).

Visual Summary:

Based on the TU BIM syllabus for Object Oriented Programming with Java (IT234), unit 11.

Discussion

Loading…