CMP228 Advanced Programming with Java

Advanced Programming with JavaUnit 217 min read

OOP in Java: Classes, Inheritance, Polymorphism & Abstraction

Unit 2 of Advanced Programming with Java covers the core principles of object-oriented programming (OOP) in Java, including classes, objects, inheritance, polymorphism, encapsulation, abstraction, and interfaces. This note explains how to design OOP systems, implement them in Java, and analyze their efficiency and real

TAKEAWAYS:

  • Classes and objects are the building blocks of OOP, where a class defines a blueprint and objects are instances of that blueprint.
  • Inheritance allows code reuse by creating a hierarchy of classes (parent-child relationships) and supports method overriding.
  • Polymorphism enables one interface to represent different underlying forms (e.g., method overloading and overriding).
  • Encapsulation hides internal data and exposes only necessary methods via access modifiers (private, public, protected).
  • Abstraction defines abstract classes and interfaces to enforce structure without implementation details.
  • Composition and aggregation are relationships where objects are combined to form larger systems.

1. Classes and Objects: The Foundation of OOP

A class is a blueprint for creating objects, while an object is an instance of a class. Classes define attributes (data members) and methods (functions) that operate on those attributes.

Key Concepts:

  • Constructors: Special methods used to initialize objects.
  • Access Modifiers: Control visibility of class members (public, private, protected, default).
  • this Keyword: Refers to the current object’s instance variables.

Example: A BankAccount Class

public class BankAccount {
    private String accountNumber;
    private double balance;

    // Constructor
    public BankAccount(String accountNumber, double initialBalance) {
        this.accountNumber = accountNumber;
        this.balance = initialBalance;
    }

    // Method to deposit money
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("Deposited: $" + amount);
        }
    }

    // Method to withdraw money
    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            System.out.println("Withdrawn: $" + amount);
        }
    }

    // Getter for balance
    public double getBalance() {
        return balance;
    }
}

Trace: Creating and Using a BankAccount Object

Step Code Executed State After Execution
1 BankAccount acc = new BankAccount("12345", 1000.0); acc.accountNumber = "12345", acc.balance = 1000.0
2 acc.deposit(500.0); acc.balance = 1500.0
3 acc.withdraw(200.0); acc.balance = 1300.0

Real-World Analogy: Bank Account in Nabil Bank

  • Class: BankAccount (blueprint for all accounts).
  • Object: A specific account (e.g., "Account 12345 with balance ₹1300").
  • Methods: deposit() and withdraw() simulate real transactions.

2. Inheritance: Code Reusability and Hierarchy

Inheritance allows a subclass (child) to inherit properties and methods from a superclass (parent). It promotes DRY (Don’t Repeat Yourself) programming.

VehicleAnimal
Hierarchical inheritance example: Vehicle → Car → SportsCar and Animal → Dog

Types of Inheritance in Java:

Type Description Example
Single One subclass inherits from one superclass. Animal → Dog
Multilevel A subclass inherits from another subclass. Vehicle → Car → SportsCar
Hierarchical Multiple subclasses inherit from one superclass. Shape → Circle, Square
Multiple Not supported in Java (use interfaces instead). -

Example: Vehicle and Car Classes

class Vehicle {
    private String model;

    public Vehicle(String model) {
        this.model = model;
    }

    public void displayModel() {
        System.out.println("Model: " + model);
    }
}

class Car extends Vehicle {
    private int numDoors;

    public Car(String model, int numDoors) {
        super(model); // Calls Vehicle constructor
        this.numDoors = numDoors;
    }

    public void displayDoors() {
        System.out.println("Number of doors: " + numDoors);
    }
}

Trace: Creating a Car Object

Step Code Executed State After Execution
1 Car myCar = new Car("Toyota", 4); myCar.model = "Toyota", myCar.numDoors = 4
2 myCar.displayModel(); Output: "Model: Toyota"
3 myCar.displayDoors(); Output: "Number of doors: 4"

Real-World Example: Daraz’s Order Processing System

  • Superclass: Order (contains orderId, customer, totalAmount).
  • Subclass: FoodOrder (extends Order and adds deliveryTime, restaurant).
  • Benefit: Reuses common order logic while adding food-specific features.

3. Polymorphism: One Interface, Multiple Forms

Polymorphism allows methods to behave differently based on the object. Two types:

  1. Compile-time (Method Overloading): Same method name, different parameters.
  2. Run-time (Method Overriding): Subclass provides a specific implementation of a superclass method.
classDiagram
    class Shape {
        <<abstract>>
        +area() double
    }
    class Circle {
        -radius: double
        +area() double
    }
    class Rectangle {
        -length: double
        -width: double
        +area() double
    }
    Shape <|-- Circle
    Shape <|-- Rectangle
    note for Circle "Overrides area() to return πr²"
    note for Rectangle "Overrides area() to return l × w"
    note for Shape "Abstract base class"
    note for Circle "Concrete implementation"
    note for Rectangle "Concrete implementation"
UML class diagram showing method overriding in Shape hierarchy (abstract base class with concrete subclasses)

Example: Method Overriding in Shape Hierarchy

class Shape {
    public double area() {
        return 0;
    }
}

class Circle extends Shape {
    private double radius;

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

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

class Rectangle extends Shape {
    private double length, width;

    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

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

Trace: Polymorphic Behavior

Shape shape1 = new Circle(5.0);
Shape shape2 = new Rectangle(4.0, 6.0);

System.out.println(shape1.area()); // Output: 78.54 (πr²)
System.out.println(shape2.area()); // Output: 24.0 (l × w)

Real-World Example: Pathao’s Ride Options

  • Superclass: Ride (abstract method calculateFare()).
  • Subclasses: BikeRide, CarRide, AutoRide (each overrides calculateFare()).
  • Benefit: Pathao’s app calls ride.calculateFare() without knowing the ride type.

4. Encapsulation: Data Hiding and Access Control

Encapsulation bundles data (attributes) and methods (behaviors) into a single unit (class) while restricting direct access to some components.

private String accountNumber0private double balance1
Encapsulated BankAccount fields with private access modifiers (hidden data)

Key Features:

  • Use private for sensitive data.
  • Provide public getters/setters for controlled access.
  • Example: A Student class hides grade but allows controlled updates.

Example: Student Class with Encapsulation

public class Student {
    private String name;
    private int grade;

    public Student(String name, int grade) {
        this.name = name;
        setGrade(grade); // Uses setter for validation
    }

    // Getter
    public String getName() {
        return name;
    }

    // Setter with validation
    public void setGrade(int grade) {
        if (grade >= 0 && grade <= 100) {
            this.grade = grade;
        } else {
            System.out.println("Invalid grade!");
        }
    }

    public int getGrade() {
        return grade;
    }
}

Trace: Setting an Invalid Grade

Step Code Executed State After Execution
1 Student student = new Student("Rohan", 85); student.name = "Rohan", student.grade = 85
2 student.setGrade(105); Output: "Invalid grade!", grade unchanged

Real-World Example: Ncell’s User Authentication

  • Encapsulation: User class hides password but provides login() method with validation.
  • Benefit: Prevents unauthorized access to sensitive data.

5. Abstraction: Hiding Complexity

Abstraction exposes only essential features and hides implementation details. Achieved via:

  • Abstract Classes: Cannot be instantiated; contain abstract methods (no body).
  • Interfaces: Pure abstraction (all methods are abstract before Java 8).

Example: Abstract Animal Class

abstract class Animal {
    private String name;

```figure
{"type":"tree","nodes":[{"v":"Animal","children":[{"v":"Dog","children":[{"v":"Puppy","highlight":true}]},{"v":"Cat"}]}],"caption":"Inheritance hierarchy showing abstract Animal class with Dog subclass and Puppy specialization"}
public Animal(String name) {
    this.name = name;
}

// Abstract method (no implementation)
public abstract void makeSound();

// Concrete method
public void sleep() {
    System.out.println(name + " is sleeping.");
}

}

class Dog extends Animal { public Dog(String name) { super(name); }

@Override
public void makeSound() {
    System.out.println("Bark!");
}

}

Trace: Creating a Dog Object

Step Code Executed State After Execution
1 Animal myDog = new Dog("Buddy"); myDog.name = "Buddy"
2 myDog.makeSound(); Output: "Bark!"
3 myDog.sleep(); Output: "Buddy is sleeping."

Real-World Example: NEPSE’s Stock Trading System

  • Interface: Trader (defines buy(), sell()).
  • Classes: RetailTrader, InstitutionalTrader (implement Trader).
  • Benefit: NEPSE’s system interacts with traders via the Trader interface without knowing their internal logic.

6. Interfaces: Contracts for Classes

Interfaces define a contract that classes must follow. A class can implement multiple interfaces (unlike inheritance).

Example: Comparable Interface

class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Person other) {
        return Integer.compare(this.age, other.age);
    }
}

Trace: Comparing Two Person Objects

Step Code Executed State After Execution
1 Person p1 = new Person("Alice", 25); p1.name = "Alice", p1.age = 25
2 Person p2 = new Person("Bob", 30); p2.name = "Bob", p2.age = 30
3 System.out.println(p1.compareTo(p2)); Output: -5 (p1 is younger)

Real-World Example: Khalti’s Payment Gateway

  • Interface: PaymentProcessor (defines processPayment()).
  • Classes: CreditCardProcessor, MobileWalletProcessor (implement PaymentProcessor).
  • Benefit: Khalti’s app calls processor.processPayment() regardless of the payment method.

7. Composition vs. Aggregation

Feature Composition Aggregation
Relationship "Has-a" (strong, part-of) "Has-a" (weak, independent)
Lifetime Child cannot exist without parent Child can exist without parent
Example Engine in Car Professor in University

Example: Composition (Engine in Car)

class Engine {
    public void start() {
        System.out.println("Engine started.");
    }
}

class Car {
    private Engine engine; // Composition

    public Car() {
        this.engine = new Engine(); // Engine is created with Car
    }

    public void start() {
        engine.start();
    }
}

Example: Aggregation (Professor in University)

class Professor {
    private String name;
    public Professor(String name) { this.name = name; }
}

class University {
    private Professor head; // Aggregation

    public University(Professor head) {
        this.head = head;
    }
}

Real-World Example: Pathao’s Driver System

  • Composition: PathaoApp contains a GPS object (GPS cannot exist without the app).
  • Aggregation: PathaoApp uses a Driver object (driver can exist independently).

In the Real World

  1. eSewa’s Payment System

    • OOP Concept: Polymorphism via PaymentMethod interface.
    • How: eSewa supports CreditCardPayment, MobilePayment, and BankTransfer classes, all implementing PaymentMethod.processPayment(). Users select a payment method, and eSewa calls the appropriate implementation without knowing the type.
  2. NTC’s Network Traffic Management

    • OOP Concept: Inheritance for device types.
    • How: NetworkDevice (superclass) defines common methods like connect() and disconnect(). Subclasses like Router, Switch, and Modem override these methods for device-specific behavior. NTC’s software manages all devices uniformly via the superclass interface.
  3. Daraz’s Order Queue System

    • OOP Concept: Queue (using LinkedList) for order processing.
    • How: Daraz uses a PriorityQueue<Order> where orders are enqueued based on priority (e.g., express vs. standard). The system dequeues and processes orders in FIFO order, but express orders jump to the front. This ensures faster delivery for premium customers while maintaining fairness for others.
    • Visualization:
flowchart TD
    A["Order Enqueued
(priority: standard)"] --> B["PriorityQueue<Order>"]
    C["Order Enqueued
(priority: express)"] --> B
    B -->|"dequeue()"| D["Process Order
(FIFO for standard)"]
    B -->|"peek()"| E["Express Order
(jumps to front)"]
PriorityQueue implementation showing express orders jumping to front (Daraz's order processing)

Exam Tip

  1. Understand the "Why" Behind OOP

    • Examiners often ask: "Why use inheritance over composition?" or "When to use abstraction?"
    • Key Points:
      • Use inheritance for "is-a" relationships (e.g., Dog is an Animal).
      • Use composition for "has-a" relationships (e.g., Car has an Engine).
      • Use abstraction to hide complexity (e.g., PaymentProcessor interface).
  2. Code Traces Are Critical

    • Always show the state after each step (like the tables above). For example:
      • If asked to trace myCar.displayDoors(), show the object’s state before and after method calls.
      • For inheritance, trace how super() calls the parent constructor.
  3. Common Pitfalls

    • Access Modifiers: Forgetting private for sensitive data (e.g., password in User class).
    • Method Overriding vs. Overloading:
      • Overriding changes the method signature (same name, parameters, return type).
      • Overloading changes parameters (same name, different parameters).
    • Abstract Classes vs. Interfaces:
      • Abstract classes can have concrete methods and fields.
      • Interfaces (before Java 8) cannot have concrete methods or fields.
  4. Diagrams in Exams

    • Draw UML class diagrams for inheritance hierarchies (e.g., Animal → Dog → Puppy).
    • Draw object diagrams to show relationships (e.g., Car contains an Engine).
    • Example UML for BankAccount:
      classDiagram
          class BankAccount {
              -String accountNumber
              -double balance
              +BankAccount(String, double)
              +deposit(double)
              +withdraw(double)
              +getBalance()
          }
  5. Real-World Scenarios

    • Expect questions like:
      • "How would you design a traffic management system for Kathmandu using OOP?" (Use Vehicle hierarchy with Car, Bike, Bus and polymorphism for calculateToll().)
      • "Explain how WhatsApp uses interfaces for different messaging services." (e.g., MessageService interface with WhatsAppService, EmailService implementations.)
  6. Practical Coding Questions

    • Be ready to write:
      • A class with encapsulation (e.g., Student with private grade and validated setters).
      • An inheritance hierarchy (e.g., Shape → Circle → Sphere).
      • An interface implementation (e.g., Comparable for sorting objects).

In the real world

  • Pathao’s Ride System: Uses polymorphism to handle different ride types (BikeRide, CarRide) under a common Ride interface, calling calculateFare() without knowing the subclass.
  • Nepali Banks (e.g., Nabil, Global IME): Apply encapsulation by hiding account details (private fields) while exposing only deposit(), withdraw(), and getBalance() methods.
  • eSewa’s Payment Gateway: Leverages abstraction to define a PaymentProcessor interface (e.g., processPayment()) implemented by CreditCardProcessor, MobileWalletProcessor, etc., without exposing internal logic.

Based on the PU BE Computer (PU) syllabus for Advanced Programming with Java (CMP228), unit 2.

Discussion

Loading…