AbstractClassExample.java

← Back to Course
Java Source File Download
/**
 * Abstract Class Example - Object-Oriented Programming
 * PT821 - State University of Zanzibar (SUZA)
 *
 * This example demonstrates:
 * - Abstract class declaration
 * - Abstract methods (no body)
 * - Concrete methods (with body)
 * - Constructors in abstract classes
 * - Implementing abstract methods in subclasses
 */

// ============================================
// ABSTRACT CLASS: Shape
// ============================================

abstract class Shape {
    protected String name;
    protected String color;

    // Constructor - abstract classes CAN have constructors
    public Shape(String name, String color) {
        this.name = name;
        this.color = color;
    }

    // Abstract methods - MUST be implemented by subclasses
    public abstract double calculateArea();
    public abstract double calculatePerimeter();

    // Concrete method - shared by all shapes
    public void displayInfo() {
        System.out.println("Shape: " + name);
        System.out.println("Color: " + color);
        System.out.println("Area: " + String.format("%.2f", calculateArea()) + " sq units");
        System.out.println("Perimeter: " + String.format("%.2f", calculatePerimeter()) + " units");
    }

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

    public String getColor() {
        return color;
    }
}

// ============================================
// CONCRETE CLASS: Circle
// ============================================

class Circle extends Shape {
    private double radius;

    public Circle(String color, double radius) {
        super("Circle", color);
        this.radius = radius;
    }

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

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

    // Circle-specific method
    public double getDiameter() {
        return 2 * radius;
    }
}

// ============================================
// CONCRETE CLASS: Rectangle
// ============================================

class Rectangle extends Shape {
    protected double width;
    protected double height;

    public Rectangle(String color, double width, double height) {
        super("Rectangle", color);
        this.width = width;
        this.height = height;
    }

    @Override
    public double calculateArea() {
        return width * height;
    }

    @Override
    public double calculatePerimeter() {
        return 2 * (width + height);
    }

    // Rectangle-specific method
    public double getDiagonal() {
        return Math.sqrt(width * width + height * height);
    }
}

// ============================================
// CONCRETE CLASS: Square (extends Rectangle)
// ============================================

class Square extends Rectangle {
    public Square(String color, double side) {
        super(color, side, side);
        this.name = "Square";  // Override the name
    }

    // Override to provide cleaner output
    @Override
    public void displayInfo() {
        System.out.println("Shape: " + name);
        System.out.println("Color: " + color);
        System.out.println("Side: " + width + " units");
        System.out.println("Area: " + String.format("%.2f", calculateArea()) + " sq units");
        System.out.println("Perimeter: " + String.format("%.2f", calculatePerimeter()) + " units");
    }
}

// ============================================
// CONCRETE CLASS: Triangle
// ============================================

class Triangle extends Shape {
    private double sideA;
    private double sideB;
    private double sideC;

    public Triangle(String color, double sideA, double sideB, double sideC) {
        super("Triangle", color);
        this.sideA = sideA;
        this.sideB = sideB;
        this.sideC = sideC;
    }

    @Override
    public double calculateArea() {
        // Using Heron's formula
        double s = (sideA + sideB + sideC) / 2;
        return Math.sqrt(s * (s - sideA) * (s - sideB) * (s - sideC));
    }

    @Override
    public double calculatePerimeter() {
        return sideA + sideB + sideC;
    }

    // Check if it's a valid triangle
    public boolean isValid() {
        return (sideA + sideB > sideC) &&
               (sideB + sideC > sideA) &&
               (sideA + sideC > sideB);
    }
}

// ============================================
// REAL-WORLD EXAMPLE: Employee Management
// ============================================

abstract class Employee {
    protected String name;
    protected String employeeId;
    protected double baseSalary;

    public Employee(String name, String employeeId, double baseSalary) {
        this.name = name;
        this.employeeId = employeeId;
        this.baseSalary = baseSalary;
    }

    // Abstract method - each employee type calculates salary differently
    public abstract double calculateSalary();

    // Abstract method - each employee type has different benefits
    public abstract String getBenefits();

    // Concrete method - shared by all employees
    public void displayPayslip() {
        System.out.println("========== PAYSLIP ==========");
        System.out.println("Employee: " + name);
        System.out.println("ID: " + employeeId);
        System.out.println("Base Salary: TZS " + String.format("%,.2f", baseSalary));
        System.out.println("Total Salary: TZS " + String.format("%,.2f", calculateSalary()));
        System.out.println("Benefits: " + getBenefits());
        System.out.println("=============================\n");
    }
}

class FullTimeEmployee extends Employee {
    private double bonus;
    private double allowances;

    public FullTimeEmployee(String name, String employeeId, double baseSalary,
                           double bonus, double allowances) {
        super(name, employeeId, baseSalary);
        this.bonus = bonus;
        this.allowances = allowances;
    }

    @Override
    public double calculateSalary() {
        return baseSalary + bonus + allowances;
    }

    @Override
    public String getBenefits() {
        return "Health Insurance, Pension, Annual Leave (30 days)";
    }
}

class PartTimeEmployee extends Employee {
    private int hoursWorked;
    private double hourlyRate;

    public PartTimeEmployee(String name, String employeeId, double hourlyRate, int hoursWorked) {
        super(name, employeeId, 0);  // No base salary for part-time
        this.hourlyRate = hourlyRate;
        this.hoursWorked = hoursWorked;
    }

    @Override
    public double calculateSalary() {
        return hourlyRate * hoursWorked;
    }

    @Override
    public String getBenefits() {
        return "Flexible hours, No fixed benefits";
    }

    @Override
    public void displayPayslip() {
        System.out.println("========== PAYSLIP ==========");
        System.out.println("Employee: " + name);
        System.out.println("ID: " + employeeId);
        System.out.println("Type: Part-Time");
        System.out.println("Hours Worked: " + hoursWorked);
        System.out.println("Hourly Rate: TZS " + String.format("%,.2f", hourlyRate));
        System.out.println("Total Salary: TZS " + String.format("%,.2f", calculateSalary()));
        System.out.println("Benefits: " + getBenefits());
        System.out.println("=============================\n");
    }
}

class ContractEmployee extends Employee {
    private int contractMonths;
    private double projectBonus;

    public ContractEmployee(String name, String employeeId, double baseSalary,
                           int contractMonths, double projectBonus) {
        super(name, employeeId, baseSalary);
        this.contractMonths = contractMonths;
        this.projectBonus = projectBonus;
    }

    @Override
    public double calculateSalary() {
        return baseSalary + projectBonus;
    }

    @Override
    public String getBenefits() {
        return "Project completion bonus, " + contractMonths + "-month contract";
    }
}

// Main class
public class AbstractClassExample {
    public static void main(String[] args) {
        System.out.println("==========================================");
        System.out.println("    ABSTRACT CLASS EXAMPLE");
        System.out.println("==========================================\n");

        // ========== PART 1: SHAPE EXAMPLE ==========
        System.out.println("=== PART 1: SHAPES ===\n");

        // Cannot instantiate abstract class
        // Shape s = new Shape("Test", "Red");  // ERROR!

        // Create array of shapes (polymorphism with abstract class)
        Shape[] shapes = new Shape[4];
        shapes[0] = new Circle("Red", 5.0);
        shapes[1] = new Rectangle("Blue", 4.0, 6.0);
        shapes[2] = new Square("Green", 5.0);
        shapes[3] = new Triangle("Yellow", 3.0, 4.0, 5.0);

        // Display info for all shapes
        for (Shape shape : shapes) {
            shape.displayInfo();
            System.out.println();
        }

        // Calculate total area of all shapes
        double totalArea = 0;
        for (Shape shape : shapes) {
            totalArea += shape.calculateArea();
        }
        System.out.println("Total area of all shapes: " + String.format("%.2f", totalArea) + " sq units\n");

        // ========== PART 2: EMPLOYEE EXAMPLE ==========
        System.out.println("=== PART 2: EMPLOYEES ===\n");

        Employee[] employees = new Employee[3];
        employees[0] = new FullTimeEmployee("Ali Hassan", "FT001", 2000000, 300000, 200000);
        employees[1] = new PartTimeEmployee("Fatma Said", "PT001", 15000, 80);
        employees[2] = new ContractEmployee("Omar Juma", "CT001", 1500000, 6, 500000);

        // Display payslips for all employees
        for (Employee emp : employees) {
            emp.displayPayslip();
        }

        // Calculate total payroll
        double totalPayroll = 0;
        for (Employee emp : employees) {
            totalPayroll += emp.calculateSalary();
        }
        System.out.println("Total Monthly Payroll: TZS " + String.format("%,.2f", totalPayroll));

        System.out.println("\n==========================================");
        System.out.println("    END OF ABSTRACT CLASS EXAMPLE");
        System.out.println("==========================================");
    }
}

/*
 * KEY CONCEPTS:
 *
 * 1. ABSTRACT CLASS:
 *    - Declared with 'abstract' keyword
 *    - Cannot be instantiated directly
 *    - Can have both abstract and concrete methods
 *    - Can have constructors, instance variables
 *
 * 2. ABSTRACT METHODS:
 *    - No method body (ends with semicolon)
 *    - MUST be implemented by concrete subclasses
 *    - Defines a contract for subclasses
 *
 * 3. WHEN TO USE:
 *    - When classes share common behavior
 *    - When you want to provide default implementation
 *    - When subclasses are closely related (IS-A relationship)
 *
 * COMPILE: javac AbstractClassExample.java
 * RUN: java AbstractClassExample
 */