Abstract Classes & Interfaces
Sometimes a parent class should never be used on its own. There is no such thing as a plain "worker" who is neither full-time nor a contractor; there is no plain "shape" that is not a circle or a rectangle. Java lets you say this with abstract classes: classes that exist only to be extended.
Interfaces go one step further. An interface is a pure contract, a list of things a class promises it can do, with no connection to any family tree. A smart bulb and a smart speaker are unrelated classes, but both can promise to be Switchable. In this lesson you will write both and learn when to choose which.
Abstract classes and methods
Mark a class abstract to stop anyone creating objects of it directly. An abstract class can declare abstract methods: methods with no body, ending in a semicolon. Every concrete (non-abstract) subclass must provide a body for each of them. An abstract class can still have fields, constructors and normal methods that use the abstract ones.
public class Payroll {
public static void main(String[] args) {
Worker[] team = {
new FullTimer("Kabir", 60000),
new Contractor("Ananya", 800, 120)
};
for (Worker w : team) {
w.printPayslip();
}
}
}
abstract class Worker {
private final String name;
Worker(String name) {
this.name = name;
}
abstract double grossPay(); // no body: subclasses must provide it
double tax() {
return grossPay() * 0.10; // a concrete method using the abstract one
}
void printPayslip() {
System.out.printf("%-7s gross Rs %,9.2f tax Rs %,8.2f net Rs %,9.2f%n",
name, grossPay(), tax(), grossPay() - tax());
}
}
class FullTimer extends Worker {
private final double monthlySalary;
FullTimer(String name, double monthlySalary) {
super(name);
this.monthlySalary = monthlySalary;
}
@Override
double grossPay() {
return monthlySalary;
}
}
class Contractor extends Worker {
private final double hourlyRate;
private final int hours;
Contractor(String name, double hourlyRate, int hours) {
super(name);
this.hourlyRate = hourlyRate;
this.hours = hours;
}
@Override
double grossPay() {
return hourlyRate * hours;
}
@Override
double tax() {
return grossPay() * 0.02; // a lower flat rate in this example
}
}Kabir gross Rs 60,000.00 tax Rs 6,000.00 net Rs 54,000.00 Ananya gross Rs 96,000.00 tax Rs 1,920.00 net Rs 94,080.00
printPayslip is written once in Worker, yet it calls grossPay(), which each subclass defines differently. The parent fixes the overall steps and the children fill in the details; this design is called the template method pattern.
The compiler enforces the rules. new Worker("x") fails with error: Worker is abstract; cannot be instantiated. A subclass that forgets grossPay fails with error: Intern is not abstract and does not override abstract method grossPay() in Worker.
Interfaces
An interface lists methods that implementing classes must provide. A class signs the contract with implements, and it can implement several interfaces at once, even though it can extend only one class. An interface can also contain:
- default methods (Java 8): methods with a body, which implementing classes get for free and may override.
- static methods (Java 8): helper methods called on the interface name.
- private methods (Java 9): helpers shared by the default methods.
- constants: every field in an interface is automatically
public static final.
Interface methods without a body are automatically public and abstract.
public class Devices {
public static void main(String[] args) {
SmartBulb bulb = new SmartBulb("Bedroom bulb");
SmartSpeaker speaker = new SmartSpeaker("Hall speaker");
Switchable[] things = {bulb, speaker};
for (Switchable s : things) {
s.turnOn();
System.out.println(s.status());
}
Chargeable c = speaker;
System.out.println("Battery: " + c.batteryPercent() + "%");
System.out.println(c.needsCharging());
System.out.println(Switchable.describeAll(things));
}
}
interface Switchable {
void turnOn(); // abstract by default
boolean isOn();
default String status() { // default method: has a body
return (isOn() ? "ON " : "OFF ") + label();
}
String label();
static String describeAll(Switchable[] items) {
int on = 0;
for (Switchable s : items) {
if (s.isOn()) on++;
}
return on + " of " + items.length + " devices are on";
}
}
interface Chargeable {
int LOW_BATTERY = 20; // a constant: public static final
int batteryPercent();
default boolean needsCharging() {
return batteryPercent() < LOW_BATTERY;
}
}
class SmartBulb implements Switchable {
private final String name;
private boolean on;
SmartBulb(String name) { this.name = name; }
public void turnOn() { on = true; }
public boolean isOn() { return on; }
public String label() { return name; }
}
class SmartSpeaker implements Switchable, Chargeable {
private final String name;
private boolean on;
SmartSpeaker(String name) { this.name = name; }
public void turnOn() { on = true; }
public boolean isOn() { return on; }
public String label() { return name; }
public int batteryPercent() { return 15; }
}ON Bedroom bulb ON Hall speaker Battery: 15% true 2 of 2 devices are on
An interface is also a type. A Switchable[] array holds a bulb and a speaker side by side, and a Chargeable variable can refer to the speaker. Code that works with Switchable does not care what the object really is, only that it keeps the promise. The implementing methods must be public; leaving public off turnOn gives error: ... attempting to assign weaker access privileges; was public.
A real interface: Comparable
The standard library is full of interfaces. One you will use often is Comparable, which tells Java how to put objects in order. Implement its single method compareTo: return a negative number if this comes first, a positive number if other comes first, and 0 if they are equal. Then Arrays.sort can sort your objects.
import java.util.Arrays;
public class Ranking {
public static void main(String[] args) {
Student[] students = {
new Student("Meera", 88),
new Student("Aarav", 95),
new Student("Rohan", 72)
};
Arrays.sort(students); // uses compareTo
System.out.println(Arrays.toString(students));
}
}
class Student implements Comparable<Student> {
final String name;
final int marks;
Student(String name, int marks) {
this.name = name;
this.marks = marks;
}
@Override
public int compareTo(Student other) {
return Integer.compare(other.marks, this.marks); // highest marks first
}
@Override
public String toString() {
return name + "(" + marks + ")";
}
}[Aarav(95), Meera(88), Rohan(72)]
Comparable<Student> uses generics: the <Student> says what it compares against. You will learn generics in Collections & Generics. Integer.compare(a, b) safely produces the negative, zero or positive result; swapping the arguments, as here, reverses the order.
Interfaces with exactly one abstract method, like Comparable, are called functional interfaces. They are the foundation of lambdas, covered in Lambdas & Functional Interfaces.
Abstract class or interface?
| Abstract class | Interface | |
|---|---|---|
| A class can have | one parent | many interfaces |
| Fields | any kind, including instance fields | only constants |
| Constructors | yes | no |
| Relationship | "is a kind of" (a contractor is a worker) | "can do" (a speaker can be switched, charged) |
A good rule: start with an interface to describe what something can do. Use an abstract class when related classes share real state (fields) and code. The two combine well: an abstract class can implement an interface and leave some methods for its subclasses.
Common mistakes
- Trying to create an abstract class or interface with
new. Create an object of a concrete subclass instead. - Forgetting to implement an abstract method. Either implement every abstract method or mark the subclass
abstracttoo. - Leaving out
publicwhen implementing interface methods. They must staypublic. - Putting instance state in an interface. Interface fields are constants. Keep changing state in classes.
- Inconsistent
compareTo. Ifa.compareTo(b)is negative,b.compareTo(a)must be positive, or sorting gives strange results.
Key points
- An
abstractclass cannot be instantiated and may declare abstract methods without bodies. - Concrete subclasses must implement every inherited abstract method.
- An interface is a contract; classes
implementsit and can implement many interfaces. - Interfaces may contain default, static and private methods, and constants.
Comparable<T>withcompareToletsArrays.sortorder your objects.- Prefer interfaces for "can do" abilities; use abstract classes for shared state and code.
Exercise
Create an interface Shape with double area(), double perimeter() and a default method describe() that returns the class name with the area and perimeter to two decimal places. Implement it in Circle (radius) and Rectangle (length, width). Put a circle of radius 3.5 and a rectangle of 12 by 8 in a Shape[] array and print each description and the total area.
Show solution
public class Shapes {
public static void main(String[] args) {
Shape[] shapes = {new Circle(3.5), new Rectangle(12, 8)};
double totalArea = 0;
for (Shape s : shapes) {
System.out.println(s.describe());
totalArea += s.area();
}
System.out.printf("Total area: %.2f%n", totalArea);
}
}
interface Shape {
double area();
double perimeter();
default String describe() {
return "%s: area %.2f, perimeter %.2f".formatted(
getClass().getSimpleName(), area(), perimeter());
}
}
class Circle implements Shape {
private final double radius;
Circle(double radius) { this.radius = radius; }
public double area() { return Math.PI * radius * radius; }
public double perimeter() { return 2 * Math.PI * radius; }
}
class Rectangle implements Shape {
private final double length;
private final double width;
Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
public double area() { return length * width; }
public double perimeter() { return 2 * (length + width); }
}Circle: area 38.48, perimeter 21.99 Rectangle: area 96.00, perimeter 40.00 Total area: 134.48
describe() is written once in the interface. It calls area() and perimeter(), which each class implements in its own way.
Quick Quiz
1. Which statement about abstract classes is true?
An abstract class can have constructors and concrete methods, but you cannot create objects of it directly.
2. How many interfaces can a Java class implement?
A class extends one class but may implement many interfaces.
3. What is a default method in an interface?
Default methods (Java 8+) provide a ready-made implementation that classes can keep or override.
4. compareTo returns a negative number. What does that mean?
Negative means this sorts earlier; positive means it sorts later.
Learn Java with a mentor
Free lessons get you started. Our Java, C & C++ Programming training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
