Skip to content
Laviyx Learn

Try

Java course menu

Java Abstract Classes and Abstract Methods

Learn abstract classes in Java: declaring abstract classes and abstract methods, why they cannot be instantiated, implementing them in subclasses, mixing abstract and concrete methods, constructors in abstract classes, the template method pattern, and abstract class vs interface.

Intermediate11 min readUpdated
On this page

Sometimes a parent class describes what every subclass must do, but cannot sensibly say how. What is the area of a generic "shape"? An abstract class expresses exactly this: it can declare abstract methods with no body, which every concrete subclass must implement, and it cannot be instantiated itself.

Declaring an abstract class#

Java
import java.util.List;

abstract class Shape {
    abstract double area();              // no body: subclasses must implement it
    abstract String name();

    String describe() {                  // a normal (concrete) method
        return String.format("%s with area %.2f", name(), area());
    }
}

class Circle extends Shape {
    private final double r;
    Circle(double r) { this.r = r; }
    @Override double area() { return Math.PI * r * r; }
    @Override String name() { return "Circle"; }
}

class Square extends Shape {
    private final double side;
    Square(double side) { this.side = side; }
    @Override double area() { return side * side; }
    @Override String name() { return "Square"; }
}

public class Main {
    public static void main(String[] args) {
        List<Shape> shapes = List.of(new Circle(1.5), new Square(3));
        for (Shape s : shapes) {
            System.out.println(s.describe());
        }
    }
}
Output
Circle with area 7.07
Square with area 9.00
  • abstract class Shape cannot be created with new.
  • abstract double area(); has no body; a subclass that does not implement it must itself be declared abstract.
  • describe() is shared, concrete code that calls the abstract methods; each subclass supplies the details.

Abstract classes cannot be instantiated#

Java
abstract class Shape {
    abstract double area();
}

public class Main {
    public static void main(String[] args) {
        Shape s = new Shape();   // error: Shape is abstract; cannot be instantiated
    }
}

You can still declare variables of type Shape and assign them any concrete subclass, which is exactly how polymorphism uses abstract classes.

Fields and constructors in abstract classes#

Unlike interfaces, abstract classes can hold state (fields) and have constructors. Subclasses call the constructor with super(...):

Java
abstract class Employee {
    private final String name;

    protected Employee(String name) {
        this.name = name;
    }

    String getName() { return name; }

    abstract double monthlyPay();

    String payslip() {
        return String.format("%s: %.2f", name, monthlyPay());
    }
}

class SalariedEmployee extends Employee {
    private final double annualSalary;

    SalariedEmployee(String name, double annualSalary) {
        super(name);
        this.annualSalary = annualSalary;
    }

    @Override
    double monthlyPay() { return annualSalary / 12; }
}

class HourlyEmployee extends Employee {
    private final double rate;
    private final int hours;

    HourlyEmployee(String name, double rate, int hours) {
        super(name);
        this.rate = rate;
        this.hours = hours;
    }

    @Override
    double monthlyPay() { return rate * hours; }
}

public class Main {
    public static void main(String[] args) {
        Employee[] staff = {
            new SalariedEmployee("Asha", 960000),
            new HourlyEmployee("Ravi", 450, 120),
        };
        for (Employee e : staff) {
            System.out.println(e.payslip());
        }
    }
}
Output
Asha: 80000.00
Ravi: 54000.00

Making the constructor protected signals that it is meant to be called only by subclasses.

The template method pattern#

An abstract class can define the overall steps of an algorithm in a final method, leaving individual steps to subclasses:

Java
abstract class Report {
    final void generate() {            // fixed sequence of steps
        System.out.println("== " + title() + " ==");
        body();
        System.out.println("-- end --");
    }

    protected abstract String title();
    protected abstract void body();
}

class SalesReport extends Report {
    @Override protected String title() { return "Sales"; }
    @Override protected void body() { System.out.println("Revenue: 1,20,000"); }
}

public class Main {
    public static void main(String[] args) {
        new SalesReport().generate();
    }
}
Output
== Sales ==
Revenue: 1,20,000
-- end --

Marking generate() as final stops subclasses from changing the overall order.

Abstract class or interface?#

Abstract classInterface
Instance fields (state)YesNo (only constants)
ConstructorsYesNo
Method bodiesYesOnly default, static and private methods
A class can extend / implementOneMany
Best forClosely related classes sharing code and stateA capability that unrelated classes can have

A common modern design uses an interface for the type (what can be done), and optionally an abstract class that implements it to share code between similar implementations. See Interfaces.

Common mistakes#

Practice#

Practice exercise

Create an abstract class Discount with an abstract method double apply(double price) and a concrete method String show(double price) returning "<price> -> <discounted>". Implement PercentOff (10% off) and FlatOff (150 off, never below 0), and show both for prices 1200 and 100.

Show solution
Java
abstract class Discount {
    abstract double apply(double price);

    String show(double price) {
        return price + " -> " + apply(price);
    }
}

class PercentOff extends Discount {
    private final double percent;
    PercentOff(double percent) { this.percent = percent; }
    @Override double apply(double price) { return price * (1 - percent / 100); }
}

class FlatOff extends Discount {
    private final double amount;
    FlatOff(double amount) { this.amount = amount; }
    @Override double apply(double price) { return Math.max(0, price - amount); }
}

public class Discounts {
    public static void main(String[] args) {
        Discount[] discounts = {new PercentOff(10), new FlatOff(150)};
        for (Discount d : discounts) {
            System.out.println(d.show(1200) + ", " + d.show(100));
        }
    }
}
Output
1200.0 -> 1080.0, 100.0 -> 90.0
1200.0 -> 1050.0, 100.0 -> 0.0

Key takeaway#

Finished this lesson?