IT234 Object Oriented Programming With Java

Object Oriented Programming With JavaUnit 118 min read

Advanced OOP: Sealed Classes, Polymorphism, Wrapper Classes & Design Patterns

Unit 11 of Object Oriented Programming With Java explores sealed classes, polymorphism (runtime vs compile-time), wrapper classes (autoboxing/unboxing), and design patterns (Singleton, Factory, Observer) with real-world examples, code traces, and exam-focused comparisons.

TAKEAWAYS:

  • Sealed classes restrict inheritance hierarchies to predefined subclasses, enabling exhaustive pattern matching (e.g., switch expressions).
  • Polymorphism in Java manifests as method overriding (runtime) and overloading (compile-time), with key differences in behavior and invocation.
  • Wrapper classes (e.g., Integer, Double) bridge primitive types to objects via autoboxing/unboxing, critical for collections and generics.
  • Design patterns (Singleton, Factory, Observer) solve recurring OOP problems like thread-safe instantiation or event handling.
  • The final modifier enforces immutability at class/method/field levels, contrasting with sealed (restrictive) and abstract (extensible) classes.
  • Real-world applications include Kathmandu traffic route optimization (Observer pattern) and Ncell’s thread-safe user session management (Singleton).


1. Sealed Classes: Restricting Inheritance Hierarchies

Sealed classes introduce a controlled form of inheritance where a class explicitly permits only specific subclasses. This enables exhaustive pattern matching (e.g., switch expressions) and improves type safety.

Key Features

  • Permitted Subclasses: Use permits clause to list allowed subclasses.
  • Final/Abstract/Sealed Subclasses: Subclasses can be final, sealed, or non-sealed.
  • Exhaustive Switch: Compiler checks if all permitted subclasses are handled.

Example: Payment System

public sealed interface Payment permits CreditCardPayment, UPIPayment, CashPayment {
    double processPayment(double amount);
}

public final class CreditCardPayment implements Payment { ... }
public sealed class UPIPayment implements Payment permits PhonePePayment, GooglePayPayment { ... }
public final class PhonePePayment extends UPIPayment { ... }

Visual: Sealed Class Hierarchy

classDiagram
    class Payment {
        <<sealed>>
        +processPayment(amount: double)
    }
    class CreditCardPayment {
        <<final>>
    }
    class UPIPayment {
        <<sealed>>
        +processPayment()
    }
    class PhonePePayment {
        <<final>>
    }
    Payment <|-- CreditCardPayment
    Payment <|-- UPIPayment
    UPIPayment <|-- PhonePePayment

Real-World Use: Ncell’s Payment Gateway

Ncell’s app uses sealed classes to restrict payment methods to CreditCard, MobileWallet, or BankTransfer. This ensures exhaustive validation during checkout.


2. Polymorphism: Runtime vs Compile-Time

Polymorphism allows objects to take many forms. Java supports two types:

  • Compile-Time (Method Overloading): Multiple methods with the same name but different parameters.
  • Runtime (Method Overriding): Subclass provides a specific implementation of a superclass method.

Comparison Table

Feature Compile-Time Polymorphism Runtime Polymorphism
Mechanism Method overloading Method overriding
Resolution At compile-time At runtime (dynamic binding)
Example add(int a, int b) vs add(double a, double b) Animal.speak() overridden in Dog
Performance Faster (no runtime lookup) Slightly slower (JVM lookup)

Example: Shape Hierarchy

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

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

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

Visual: Runtime Polymorphism Trace

sequenceDiagram
    participant Shape as Shape (Reference)
    participant Circle as Circle (Object)
    participant Square as Square (Object)
    Shape->>Circle: draw() called
    Circle->>Circle: "Drawing circle" executed
    Shape->>Square: draw() called
    Square->>Square: "Drawing square" executed

Real-World Use: Daraz’s Order Processing

Daraz uses runtime polymorphism to handle different order types (ElectronicsOrder, GroceriesOrder). Each subclass overrides process() to apply specific business logic (e.g., discounts).


3. Wrapper Classes and Autoboxing/Unboxing

Wrapper classes convert primitive types (int, double) to objects (Integer, Double), enabling use in collections (e.g., ArrayList<Integer>).

Key Wrapper Classes

Primitive Wrapper Class Autoboxing Example
int Integer List<Integer> list = new ArrayList<>(); list.add(5);
double Double Double d = 3.14;
char Character Character c = 'A';

Example: Autoboxing/Unboxing

int num = 10;          // Primitive
Integer numObj = num;  // Autoboxing (int → Integer)
int numPrim = numObj;  // Unboxing (Integer → int)

Visual: Autoboxing Flow

flowchart TD
    A["int num = 10"] -->|"Autoboxing"| B["Integer numObj"]
    B -->|"Unboxing"| C["int numPrim"]

Real-World Use: eSewa’s Transaction Logs

eSewa stores transaction amounts as BigDecimal (wrapper for double) to avoid floating-point precision errors in financial calculations.


4. Design Patterns: Singleton, Factory, Observer

Design patterns provide reusable solutions to common OOP problems.

A. Singleton Pattern

Ensures a class has only one instance and provides a global point of access. Example: Database Connection

public class Database {
    private static Database instance;
    private Database() {} // Private constructor
    public static Database getInstance() {
        if (instance == null) instance = new Database();
        return instance;
    }
}

Visual: Singleton Lifecycle

stateDiagram-v2
    [*] --> NoInstance: Initial State
    NoInstance --> Instance: getInstance() called
    Instance --> [*]: Instance used

B. Factory Pattern

Delegates instantiation to subclasses. Example: Vehicle Factory

interface Vehicle { void drive(); }
class Car implements Vehicle { public void drive() { ... } }
class Bike implements Vehicle { public void drive() { ... } }

class VehicleFactory {
    public Vehicle getVehicle(String type) {
        return switch(type) {
            case "car" -> new Car();
            case "bike" -> new Bike();
            default -> throw new IllegalArgumentException();
        };
    }
}

C. Observer Pattern

Notifies observers (e.g., UI components) of state changes. Example: Stock Market Alerts

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) {
        observers.forEach(o -> o.update(news));
    }
}

Visual: Observer Pattern

classDiagram
    class NewsAgency {
        +addObserver(o: Observer)
        +notifyObservers(news: String)
    }
    class Observer {
        <<interface>>
        +update(news: String)
    }
    class StockApp {
        -update(news: String)
    }
    NewsAgency "1" --> "*" Observer : notifies
    Observer <|-- StockApp

Real-World Use: Pathao’s Ride Updates

Pathao uses the Observer pattern to notify drivers of new ride requests in real-time.


Exam Tip

  1. Sealed Classes: Always list permitted subclasses in the permits clause. Exhaustive switch is a common exam question.
  2. Polymorphism: Differentiate between overloading (compile-time) and overriding (runtime). Use UML diagrams to show inheritance.
  3. Wrapper Classes: Remember autoboxing/unboxing rules. Collections require wrapper types (e.g., List<Integer>).
  4. Design Patterns:
    • Singleton: Private constructor + static getInstance().
    • Factory: Use switch or if-else for type-based instantiation.
    • Observer: Focus on the update() method and observer registration.
  5. Code Traces: For polymorphism, trace method calls step-by-step (e.g., Shape reference pointing to Circle object).
  6. Real-World Links: Connect sealed classes to payment systems, polymorphism to order processing, and design patterns to apps like Pathao or eSewa.

Singleton pattern class diagram**Private constructor and static instance (Image: Hpesoj00, CC BY-SA 4.0, via Wikimedia Commons)

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

Discussion

Loading…