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.
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#
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());
}
}
}Circle with area 7.07
Square with area 9.00abstract class Shapecannot be created withnew.abstract double area();has no body; a subclass that does not implement it must itself be declaredabstract.describe()is shared, concrete code that calls the abstract methods; each subclass supplies the details.
Abstract classes cannot be instantiated#
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(...):
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());
}
}
}Asha: 80000.00
Ravi: 54000.00Making 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:
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();
}
}== Sales ==
Revenue: 1,20,000
-- end --Marking generate() as final stops subclasses from changing the overall order.
Abstract class or interface?#
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
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));
}
}
}1200.0 -> 1080.0, 100.0 -> 90.0
1200.0 -> 1050.0, 100.0 -> 0.0Key takeaway#
Finished this lesson?