OOP Intermediate
Abstract Classes
Partial Implementation
You have a Shape class with area() method. But "Shape" itself has no area - only Circle and Rectangle do. An abstract class lets you define the method without implementing it, forcing subclasses to provide their own implementation.
Basic abstract class
Define a class that can't be instantiated.
// Basic Abstract Class and Method
public class BasicAbstract {
public static void main(String[] args) {
System.out.println("=== Abstract Class ===\n");
// Cannot instantiate abstract class!
// Animal animal = new Animal(); // ERROR: Animal is abstract
// Must use concrete subclasses
Dog dog = new Dog();
Cat cat = new Cat();
dog.makeSound();
cat.makeSound();
System.out.println("\n=== Using Parent Reference ===");
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.makeSound();
a2.makeSound();
System.out.println("\n=== Why Use Abstract? ===");
System.out.println("""
Abstract classes:
1. Define a CONTRACT
- "All animals must make a sound"
- Subclasses MUST implement makeSound()
2. Prevent incomplete objects
- Cannot create a generic "Animal"
- Must be a specific animal type
3. Provide common structure
- Shared fields and methods
- Specific behavior in subclasses
""");
}
}
// Abstract class - cannot instantiate
abstract class Animal {
String name;
// Abstract method - no body!
abstract void makeSound();
// Notice: no { } curly braces, just semicolon!
}
// Concrete class - MUST implement abstract methods
class Dog extends Animal {
static String dogSound = ;
@Override
void makeSound() {
System.out.println(dogSound);
}
}
class Cat extends Animal {
static String catSound = ;
@Override
void makeSound() {
System.out.println(catSound);
}
}
// What if we forget to implement?
// class Bird extends Animal {
// // No makeSound() implementation
// // ERROR: Bird must implement abstract method makeSound()
// }
// Basic Abstract Class and Method
public class BasicAbstract {
public static void main(String[] args) {
System.out.println("=== Abstract Class ===\n");
// Cannot instantiate abstract class!
// Animal animal = new Animal(); // ERROR: Animal is abstract
// Must use concrete subclasses
Dog dog = new Dog();
Cat cat = new Cat();
dog.makeSound();
cat.makeSound();
System.out.println("\n=== Using Parent Reference ===");
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.makeSound();
a2.makeSound();
System.out.println("\n=== Why Use Abstract? ===");
System.out.println("""
Abstract classes:
1. Define a CONTRACT
- "All animals must make a sound"
- Subclasses MUST implement makeSound()
2. Prevent incomplete objects
- Cannot create a generic "Animal"
- Must be a specific animal type
3. Provide common structure
- Shared fields and methods
- Specific behavior in subclasses
""");
}
}
// Abstract class - cannot instantiate
abstract class Animal {
String name;
// Abstract method - no body!
abstract void makeSound();
// Notice: no { } curly braces, just semicolon!
}
// Concrete class - MUST implement abstract methods
class Dog extends Animal {
static String dogSound = ;
@Override
void makeSound() {
System.out.println(dogSound);
}
}
class Cat extends Animal {
static String catSound = ;
@Override
void makeSound() {
System.out.println(catSound);
}
}
// What if we forget to implement?
// class Bird extends Animal {
// // No makeSound() implementation
// // ERROR: Bird must implement abstract method makeSound()
// }
// Basic Abstract Class and Method
public class BasicAbstract {
public static void main(String[] args) {
System.out.println("=== Abstract Class ===\n");
// Cannot instantiate abstract class!
// Animal animal = new Animal(); // ERROR: Animal is abstract
// Must use concrete subclasses
Dog dog = new Dog();
Cat cat = new Cat();
dog.makeSound();
cat.makeSound();
System.out.println("\n=== Using Parent Reference ===");
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.makeSound();
a2.makeSound();
System.out.println("\n=== Why Use Abstract? ===");
System.out.println("""
Abstract classes:
1. Define a CONTRACT
- "All animals must make a sound"
- Subclasses MUST implement makeSound()
2. Prevent incomplete objects
- Cannot create a generic "Animal"
- Must be a specific animal type
3. Provide common structure
- Shared fields and methods
- Specific behavior in subclasses
""");
}
}
// Abstract class - cannot instantiate
abstract class Animal {
String name;
// Abstract method - no body!
abstract void makeSound();
// Notice: no { } curly braces, just semicolon!
}
// Concrete class - MUST implement abstract methods
class Dog extends Animal {
static String dogSound = ;
@Override
void makeSound() {
System.out.println(dogSound);
}
}
class Cat extends Animal {
static String catSound = ;
@Override
void makeSound() {
System.out.println(catSound);
}
}
// What if we forget to implement?
// class Bird extends Animal {
// // No makeSound() implementation
// // ERROR: Bird must implement abstract method makeSound()
// }
// Basic Abstract Class and Method
public class BasicAbstract {
public static void main(String[] args) {
System.out.println("=== Abstract Class ===\n");
// Cannot instantiate abstract class!
// Animal animal = new Animal(); // ERROR: Animal is abstract
// Must use concrete subclasses
Dog dog = new Dog();
Cat cat = new Cat();
dog.makeSound();
cat.makeSound();
System.out.println("\n=== Using Parent Reference ===");
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.makeSound();
a2.makeSound();
System.out.println("\n=== Why Use Abstract? ===");
System.out.println("""
Abstract classes:
1. Define a CONTRACT
- "All animals must make a sound"
- Subclasses MUST implement makeSound()
2. Prevent incomplete objects
- Cannot create a generic "Animal"
- Must be a specific animal type
3. Provide common structure
- Shared fields and methods
- Specific behavior in subclasses
""");
}
}
// Abstract class - cannot instantiate
abstract class Animal {
String name;
// Abstract method - no body!
abstract void makeSound();
// Notice: no { } curly braces, just semicolon!
}
// Concrete class - MUST implement abstract methods
class Dog extends Animal {
static String dogSound = ;
@Override
void makeSound() {
System.out.println(dogSound);
}
}
class Cat extends Animal {
static String catSound = ;
@Override
void makeSound() {
System.out.println(catSound);
}
}
// What if we forget to implement?
// class Bird extends Animal {
// // No makeSound() implementation
// // ERROR: Bird must implement abstract method makeSound()
// }
// Basic Abstract Class and Method
public class BasicAbstract {
public static void main(String[] args) {
System.out.println("=== Abstract Class ===\n");
// Cannot instantiate abstract class!
// Animal animal = new Animal(); // ERROR: Animal is abstract
// Must use concrete subclasses
Dog dog = new Dog();
Cat cat = new Cat();
dog.makeSound();
cat.makeSound();
System.out.println("\n=== Using Parent Reference ===");
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.makeSound();
a2.makeSound();
System.out.println("\n=== Why Use Abstract? ===");
System.out.println("""
Abstract classes:
1. Define a CONTRACT
- "All animals must make a sound"
- Subclasses MUST implement makeSound()
2. Prevent incomplete objects
- Cannot create a generic "Animal"
- Must be a specific animal type
3. Provide common structure
- Shared fields and methods
- Specific behavior in subclasses
""");
}
}
// Abstract class - cannot instantiate
abstract class Animal {
String name;
// Abstract method - no body!
abstract void makeSound();
// Notice: no { } curly braces, just semicolon!
}
// Concrete class - MUST implement abstract methods
class Dog extends Animal {
static String dogSound = ;
@Override
void makeSound() {
System.out.println(dogSound);
}
}
class Cat extends Animal {
static String catSound = ;
@Override
void makeSound() {
System.out.println(catSound);
}
}
// What if we forget to implement?
// class Bird extends Animal {
// // No makeSound() implementation
// // ERROR: Bird must implement abstract method makeSound()
// }
// Basic Abstract Class and Method
public class BasicAbstract {
public static void main(String[] args) {
System.out.println("=== Abstract Class ===\n");
// Cannot instantiate abstract class!
// Animal animal = new Animal(); // ERROR: Animal is abstract
// Must use concrete subclasses
Dog dog = new Dog();
Cat cat = new Cat();
dog.makeSound();
cat.makeSound();
System.out.println("\n=== Using Parent Reference ===");
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.makeSound();
a2.makeSound();
System.out.println("\n=== Why Use Abstract? ===");
System.out.println("""
Abstract classes:
1. Define a CONTRACT
- "All animals must make a sound"
- Subclasses MUST implement makeSound()
2. Prevent incomplete objects
- Cannot create a generic "Animal"
- Must be a specific animal type
3. Provide common structure
- Shared fields and methods
- Specific behavior in subclasses
""");
}
}
// Abstract class - cannot instantiate
abstract class Animal {
String name;
// Abstract method - no body!
abstract void makeSound();
// Notice: no { } curly braces, just semicolon!
}
// Concrete class - MUST implement abstract methods
class Dog extends Animal {
static String dogSound = ;
@Override
void makeSound() {
System.out.println(dogSound);
}
}
class Cat extends Animal {
static String catSound = ;
@Override
void makeSound() {
System.out.println(catSound);
}
}
// What if we forget to implement?
// class Bird extends Animal {
// // No makeSound() implementation
// // ERROR: Bird must implement abstract method makeSound()
// }
// Basic Abstract Class and Method
public class BasicAbstract {
public static void main(String[] args) {
System.out.println("=== Abstract Class ===\n");
// Cannot instantiate abstract class!
// Animal animal = new Animal(); // ERROR: Animal is abstract
// Must use concrete subclasses
Dog dog = new Dog();
Cat cat = new Cat();
dog.makeSound();
cat.makeSound();
System.out.println("\n=== Using Parent Reference ===");
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.makeSound();
a2.makeSound();
System.out.println("\n=== Why Use Abstract? ===");
System.out.println("""
Abstract classes:
1. Define a CONTRACT
- "All animals must make a sound"
- Subclasses MUST implement makeSound()
2. Prevent incomplete objects
- Cannot create a generic "Animal"
- Must be a specific animal type
3. Provide common structure
- Shared fields and methods
- Specific behavior in subclasses
""");
}
}
// Abstract class - cannot instantiate
abstract class Animal {
String name;
// Abstract method - no body!
abstract void makeSound();
// Notice: no { } curly braces, just semicolon!
}
// Concrete class - MUST implement abstract methods
class Dog extends Animal {
static String dogSound = ;
@Override
void makeSound() {
System.out.println(dogSound);
}
}
class Cat extends Animal {
static String catSound = ;
@Override
void makeSound() {
System.out.println(catSound);
}
}
// What if we forget to implement?
// class Bird extends Animal {
// // No makeSound() implementation
// // ERROR: Bird must implement abstract method makeSound()
// }
// Basic Abstract Class and Method
public class BasicAbstract {
public static void main(String[] args) {
System.out.println("=== Abstract Class ===\n");
// Cannot instantiate abstract class!
// Animal animal = new Animal(); // ERROR: Animal is abstract
// Must use concrete subclasses
Dog dog = new Dog();
Cat cat = new Cat();
dog.makeSound();
cat.makeSound();
System.out.println("\n=== Using Parent Reference ===");
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.makeSound();
a2.makeSound();
System.out.println("\n=== Why Use Abstract? ===");
System.out.println("""
Abstract classes:
1. Define a CONTRACT
- "All animals must make a sound"
- Subclasses MUST implement makeSound()
2. Prevent incomplete objects
- Cannot create a generic "Animal"
- Must be a specific animal type
3. Provide common structure
- Shared fields and methods
- Specific behavior in subclasses
""");
}
}
// Abstract class - cannot instantiate
abstract class Animal {
String name;
// Abstract method - no body!
abstract void makeSound();
// Notice: no { } curly braces, just semicolon!
}
// Concrete class - MUST implement abstract methods
class Dog extends Animal {
static String dogSound = ;
@Override
void makeSound() {
System.out.println(dogSound);
}
}
class Cat extends Animal {
static String catSound = ;
@Override
void makeSound() {
System.out.println(catSound);
}
}
// What if we forget to implement?
// class Bird extends Animal {
// // No makeSound() implementation
// // ERROR: Bird must implement abstract method makeSound()
// }
// Basic Abstract Class and Method
public class BasicAbstract {
public static void main(String[] args) {
System.out.println("=== Abstract Class ===\n");
// Cannot instantiate abstract class!
// Animal animal = new Animal(); // ERROR: Animal is abstract
// Must use concrete subclasses
Dog dog = new Dog();
Cat cat = new Cat();
dog.makeSound();
cat.makeSound();
System.out.println("\n=== Using Parent Reference ===");
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.makeSound();
a2.makeSound();
System.out.println("\n=== Why Use Abstract? ===");
System.out.println("""
Abstract classes:
1. Define a CONTRACT
- "All animals must make a sound"
- Subclasses MUST implement makeSound()
2. Prevent incomplete objects
- Cannot create a generic "Animal"
- Must be a specific animal type
3. Provide common structure
- Shared fields and methods
- Specific behavior in subclasses
""");
}
}
// Abstract class - cannot instantiate
abstract class Animal {
String name;
// Abstract method - no body!
abstract void makeSound();
// Notice: no { } curly braces, just semicolon!
}
// Concrete class - MUST implement abstract methods
class Dog extends Animal {
static String dogSound = ;
@Override
void makeSound() {
System.out.println(dogSound);
}
}
class Cat extends Animal {
static String catSound = ;
@Override
void makeSound() {
System.out.println(catSound);
}
}
// What if we forget to implement?
// class Bird extends Animal {
// // No makeSound() implementation
// // ERROR: Bird must implement abstract method makeSound()
// }
abstract class can't be instantiated. Can have abstract and concrete methods.
Mix abstract and concrete methods
Provide some implementation, require the rest.
// Mixing Abstract and Concrete Methods
public class AbstractWithConcrete {
public static void main(String[] args) {
System.out.println("=== Abstract Class with Concrete Methods ===\n");
Dog dog = new Dog("Buddy");
Cat cat = new Cat("Whiskers");
System.out.println("--- Dog ---");
dog.introduce(); // Concrete (inherited)
dog.makeSound(); // Abstract (implemented)
dog.sleep(); // Concrete (inherited)
System.out.println("\n--- Cat ---");
cat.introduce();
cat.makeSound();
cat.sleep();
System.out.println("\n=== Bird Overrides Concrete Method ===");
Bird bird = new Bird("Tweety");
bird.introduce();
bird.makeSound();
bird.sleep(); // Overridden!
System.out.println("\n=== Abstract vs Concrete ===");
System.out.println("""
Abstract methods:
- MUST be overridden
- No default implementation
- Subclass provides behavior
Concrete methods:
- CAN be overridden (optional)
- Have default implementation
- Subclass uses or replaces
""");
}
}
abstract class Animal {
protected String name;
// Constructor in abstract class
Animal(String name) {
this.name = name;
}
// CONCRETE method - has implementation
void introduce() {
System.out.println("Hi, I'm " + name);
}
// CONCRETE method - shared behavior
void sleep() {
System.out.println(name + " is sleeping... Zzz");
}
// ABSTRACT method - subclass must implement
abstract void makeSound();
}
class Dog extends Animal {
Dog(String name) {
super(name);
}
// MUST implement abstract method
@Override
void makeSound() {
System.out.println(name + " barks: Woof! Woof!");
}
// Does NOT override sleep() - uses inherited version
}
class Cat extends Animal {
Cat(String name) {
super(name);
}
@Override
void makeSound() {
System.out.println(name + " meows: Meow!");
}
// Also uses inherited sleep()
}
class Bird extends Animal {
Bird(String name) {
super(name);
}
@Override
void makeSound() {
System.out.println(name + " chirps: Tweet! Tweet!");
}
// OPTIONAL: Override concrete method
@Override
void sleep() {
System.out.println(name + " perches and sleeps");
}
}
Abstract methods have no body. Concrete methods have implementation.
Constructors in abstract classes
Abstract classes can have constructors for subclasses.
// Constructors in Abstract Classes
public class AbstractConstructor {
public static void main(String[] args) {
System.out.println("=== Abstract Class Constructors ===\n");
// Create employees
Manager manager = new Manager("Alice", 85000, "Engineering");
Developer developer = new Developer("Bob", 75000, "Java");
System.out.println("--- Manager ---");
manager.displayInfo();
manager.work();
System.out.println("\n--- Developer ---");
developer.displayInfo();
developer.work();
System.out.println("\n=== Constructor Chain ===");
System.out.println("""
When creating Manager("Alice", 85000, "Engineering"):
1. Manager constructor called
2. super(name, salary) → Employee constructor
3. Employee initializes name, salary, id
4. Manager initializes department
Even though Employee is abstract,
its constructor runs!
""");
System.out.println("=== Multiple Constructors ===\n");
// Different constructors
Product p1 = new Book("Clean Code", 44.99, "Robert Martin");
Product p2 = new Book("Effective Java"); // Uses default price
System.out.println("Book 1:");
p1.displayInfo();
System.out.println("\nBook 2:");
p2.displayInfo();
}
}
// Abstract class with constructor
abstract class Employee {
protected String name;
protected double salary;
protected int employeeId;
private static int nextId = 1000;
// Constructor - called by subclasses
Employee(String name, double salary) {
this.name = name;
this.salary = salary;
this.employeeId = nextId++;
System.out.println("Employee constructor: Creating " + name);
}
// Concrete method
void displayInfo() {
System.out.println("ID: " + employeeId);
System.out.println("Name: " + name);
System.out.println("Salary: $" + salary);
}
// Abstract method
abstract void work();
}
class Manager extends Employee {
private String department;
Manager(String name, double salary, String department) {
super(name, salary);
this.department = department;
System.out.println("Manager constructor: Assigned to " + department);
}
@Override
void displayInfo() {
super.displayInfo();
System.out.println("Department: " + department);
}
@Override
void work() {
System.out.println(name + " is managing the " + department + " team");
}
}
class Developer extends Employee {
private String language;
Developer(String name, double salary, String language) {
super(name, salary);
this.language = language;
}
@Override
void displayInfo() {
super.displayInfo();
System.out.println("Language: " + language);
}
@Override
void work() {
System.out.println(name + " is coding in " + language);
}
}
// Abstract class with multiple constructors
abstract class Product {
protected String name;
protected double price;
// Constructor with all params
Product(String name, double price) {
this.name = name;
this.price = price;
}
// Constructor with default price
Product(String name) {
this(name, 0.0); // Calls other constructor
}
void displayInfo() {
System.out.println("Product: " + name);
System.out.println("Price: $" + price);
}
abstract double calculateTax();
}
class Book extends Product {
private String author;
Book(String name, double price, String author) {
super(name, price);
this.author = author;
}
Book(String name) {
super(name); // Uses Product(String) constructor
this.author = "Unknown";
}
@Override
void displayInfo() {
super.displayInfo();
System.out.println("Author: " + author);
}
@Override
double calculateTax() {
return price * 0.05; // 5% tax on books
}
}
Subclass constructor calls super() to initialize parent's fields.
Abstract class hierarchies
Chain abstract classes for layered abstraction.
// Abstract Class Hierarchies
public class AbstractChain {
public static void main(String[] args) {
System.out.println("=== Abstract Class Hierarchy ===\n");
// Create concrete vehicles
Car car = new Car("Toyota", "Camry", 4);
Motorcycle bike = new Motorcycle("Harley", "Sportster", true);
System.out.println("--- Car ---");
car.displayInfo();
car.start();
car.accelerate();
car.brake();
System.out.println("\n--- Motorcycle ---");
bike.displayInfo();
bike.start();
bike.accelerate();
bike.brake();
System.out.println("\n=== Hierarchy Structure ===");
System.out.println("""
Vehicle (abstract) ← Base abstract class
│
├── MotorVehicle (abstract) ← Still abstract!
│ │
│ ├── Car (concrete)
│ └── Motorcycle (concrete)
│
└── Bicycle (concrete) ← Implements all abstract
""");
System.out.println("=== Bicycle (extends Vehicle directly) ===\n");
Bicycle bicycle = new Bicycle("Trek", "FX3");
bicycle.displayInfo();
bicycle.start();
bicycle.accelerate();
bicycle.brake();
}
}
// Level 1: Top abstract class
abstract class Vehicle {
protected String brand;
protected String model;
Vehicle(String brand, String model) {
this.brand = brand;
this.model = model;
}
void displayInfo() {
System.out.println("Brand: " + brand);
System.out.println("Model: " + model);
}
// Abstract methods - ALL subclasses must implement
abstract void start();
abstract void accelerate();
abstract void brake();
}
// Level 2: Abstract subclass
abstract class MotorVehicle extends Vehicle {
protected String engineType;
MotorVehicle(String brand, String model, String engine) {
super(brand, model);
this.engineType = engine;
}
@Override
void displayInfo() {
super.displayInfo();
System.out.println("Engine: " + engineType);
}
// Implement ONE abstract method
@Override
void start() {
System.out.println("Starting " + engineType + " engine...");
}
// Leave accelerate() and brake() still abstract!
// Subclasses must implement them
}
// Level 3: Concrete class
class Car extends MotorVehicle {
private int numDoors;
Car(String brand, String model, int doors) {
super(brand, model, "V6");
this.numDoors = doors;
}
@Override
void displayInfo() {
super.displayInfo();
System.out.println("Doors: " + numDoors);
}
// Must implement remaining abstract methods
@Override
void accelerate() {
System.out.println("Car accelerating smoothly...");
}
@Override
void brake() {
System.out.println("Car braking with ABS...");
}
}
class Motorcycle extends MotorVehicle {
private boolean hasSidecar;
Motorcycle(String brand, String model, boolean sidecar) {
super(brand, model, "V-Twin");
this.hasSidecar = sidecar;
}
@Override
void displayInfo() {
super.displayInfo();
System.out.println("Sidecar: " + (hasSidecar ? "Yes" : "No"));
}
@Override
void accelerate() {
System.out.println("Motorcycle accelerating fast!");
}
@Override
void brake() {
System.out.println("Motorcycle braking...");
}
}
// Alternative: Extends Vehicle directly
class Bicycle extends Vehicle {
Bicycle(String brand, String model) {
super(brand, model);
}
// Must implement ALL THREE abstract methods
@Override
void start() {
System.out.println("Getting on the bicycle...");
}
@Override
void accelerate() {
System.out.println("Pedaling faster!");
}
@Override
void brake() {
System.out.println("Squeezing hand brakes...");
}
}
Abstract class can extend another abstract class. Concrete class must implement all.
Template method pattern
Define algorithm structure, let subclasses fill in steps.
// Template Method Design Pattern
public class TemplateMethod {
public static void main(String[] args) {
System.out.println("=== Template Method Pattern ===\n");
System.out.println("--- Making Coffee ---");
Beverage coffee = new Coffee();
coffee.prepare();
System.out.println("\n--- Making Tea ---");
Beverage tea = new Tea();
tea.prepare();
System.out.println("\n=== Pattern Explanation ===");
System.out.println("""
Template Method Pattern:
1. Abstract class defines the ALGORITHM skeleton
2. Some steps are concrete (shared)
3. Some steps are abstract (customized)
4. Subclasses fill in the blanks
prepare() is the TEMPLATE:
- boilWater() ← Same for all
- brew() ← Different for each
- pourInCup() ← Same for all
- addCondiments() ← Different for each
""");
System.out.println("=== Another Example: Report Generator ===\n");
Report pdf = new PDFReport();
Report html = new HTMLReport();
System.out.println("--- PDF Report ---");
pdf.generate();
System.out.println("\n--- HTML Report ---");
html.generate();
}
}
// Template Method in Beverage class
abstract class Beverage {
// TEMPLATE METHOD - defines the algorithm
final void prepare() {
boilWater(); // Concrete - same for all
brew(); // Abstract - subclass implements
pourInCup(); // Concrete - same for all
addCondiments(); // Abstract - subclass implements
}
// Concrete methods - shared implementation
void boilWater() {
System.out.println("1. Boiling water");
}
void pourInCup() {
System.out.println("3. Pouring into cup");
}
// Abstract methods - subclass provides
abstract void brew();
abstract void addCondiments();
}
class Coffee extends Beverage {
@Override
void brew() {
System.out.println("2. Dripping coffee through filter");
}
@Override
void addCondiments() {
System.out.println("4. Adding sugar and milk");
}
}
class Tea extends Beverage {
@Override
void brew() {
System.out.println("2. Steeping tea bag");
}
@Override
void addCondiments() {
System.out.println("4. Adding lemon and honey");
}
}
// Another Template Method example
abstract class Report {
// Template method
final void generate() {
openDocument();
writeHeader(); // Abstract
writeContent(); // Abstract
writeFooter(); // Abstract
closeDocument();
}
void openDocument() {
System.out.println("Opening document...");
}
void closeDocument() {
System.out.println("Closing document.");
}
// Abstract steps
abstract void writeHeader();
abstract void writeContent();
abstract void writeFooter();
}
class PDFReport extends Report {
@Override
void writeHeader() {
System.out.println("Writing PDF header with metadata");
}
@Override
void writeContent() {
System.out.println("Writing PDF content with images");
}
@Override
void writeFooter() {
System.out.println("Writing PDF footer with page numbers");
}
}
class HTMLReport extends Report {
@Override
void writeHeader() {
System.out.println("Writing <head> with CSS");
}
@Override
void writeContent() {
System.out.println("Writing <body> with HTML tags");
}
@Override
void writeFooter() {
System.out.println("Writing <footer> with links");
}
}
Concrete method calls abstract methods - subclasses provide the specifics.
Exercise: Practical.java
Build a document processing system with abstract classes