1. Abstract class: a refresher
Before moving on to the comparison, let’s refresh what an abstract class is.
An abstract class is a class that cannot be instantiated directly (you can’t write new Animal()) but can contain both concrete (with implementation) and abstract (without implementation) methods. An abstract class is often used as a base for other classes that inherit its behavior and/or are required to implement certain methods.
For example, if our application has different kinds of transport, we can introduce an abstract class Transport:
public abstract class Transport {
private String model;
public Transport(String model) {
this.model = model;
}
public String getModel() {
return model;
}
// Abstract method — no implementation, only declaration
public abstract void move();
// Regular method — has implementation
public void printInfo() {
System.out.println("Transport model: " + model);
}
}
Features of an abstract class:
- Can contain fields (state).
- Can contain implemented methods.
- Can contain abstract methods (must be implemented in subclasses).
- Cannot be instantiated directly.
- Used for shared behavior and state.
2. Interface: a refresher
An interface is a set of methods a class must implement. An interface does not describe state (it cannot have regular fields, only constants) and does not contain method implementations (until Java 8). An interface is a pure contract: “If you implement me, you must be able to do this.”
Example of an interface:
public interface Movable {
void move(int x, int y);
}
Features of an interface:
- Does not contain state (only public static final constants).
- Until Java 8 — only abstract methods (since Java 8, default and static methods appeared, but more on that later).
- Methods are always public abstract by default.
- A class can implement multiple interfaces.
- Used to describe capabilities — what a class can do.
3. Comparison table: abstract class vs interface
Time to compare these two tools head to head! Here’s a clear table:
| Feature | Abstract class | Interface |
|---|---|---|
| Syntax | |
|
| Can you instantiate it? | No | No |
| Can it contain concrete methods? | Yes | Until Java 8 — no; since Java 8 — only default/static |
| Can it contain abstract methods? | Yes | Yes (before Java 8, all methods are abstract) |
| Can it contain fields (state)? | Yes (any fields) | Only public static final (constants) |
| Can it contain constructors? | Yes | No |
| Inheritance | Only one abstract/regular class | Can implement multiple interfaces |
| Method modifiers | Any (public, protected, private) | Methods are public abstract by default. Since Java 9 you can add private methods for use inside the interface |
| Inheritance via | |
|
| Typical use | Shared implementation and state | Describing capabilities, roles |
| Examples from the JDK | |
|
4. When to use an interface, and when an abstract class?
Use an interface when:
- You want to describe what a class can do without caring how it does it.
- You need a class to implement several independent capabilities.
- Example: Comparable (can be compared), Serializable (can be serialized), Runnable (can be run on a thread).
Use an abstract class when:
- You want to define shared implementation and state that all subclasses will have.
- You need all subclasses to have certain fields or methods with implementation.
- Inheritance is strictly one-to-one: a class can extend only one class (concrete or abstract).
Real-life analogies
- Interface — like a driver’s license: if you have one, you can drive a car, but it doesn’t say which car or how you do it.
- Abstract class — like a general car blueprint: all cars have a steering wheel, pedals, an engine, but each brand implements the details in its own way.
5. Examples from the Java standard library
Interface: Comparable
public interface Comparable<T> {
int compareTo(T o);
}
Any class that implements this interface must implement the compareTo method. For example, String, Integer, LocalDate, and many others.
Abstract class: AbstractList
public abstract class AbstractList<E> implements List<E> {
// Default implementations of some List methods
// Some methods are left abstract
}
AbstractList already implements part of collection behavior (for example, add/remove methods) but leaves some methods abstract so that subclasses can implement them in their own way.
6. Code examples: comparison in practice
Interface
Let’s create an interface and a class that implements it.
public interface Printable {
void print();
}
public class Document implements Printable {
@Override
public void print() {
System.out.println("Printing a document...");
}
}
Abstract class
Now an abstract class and its subclass.
public abstract class Machine {
public void turnOn() {
System.out.println("The machine is on.");
}
public abstract void work();
}
public class Printer extends Machine {
@Override
public void work() {
System.out.println("The printer is printing...");
}
}
A class that uses both: implements an interface and extends an abstract class
public class SmartPrinter extends Machine implements Printable {
@Override
public void work() {
System.out.println("Smart printer is working...");
}
@Override
public void print() {
System.out.println("Smart printer is printing...");
}
}
7. Multiple interface implementation: why it’s powerful
In Java, a class can extend only one class (abstract or regular) but can implement any number of interfaces! This enables flexible, extensible architectures.
public interface Scannable {
void scan();
}
public class MultiFunctionPrinter extends Machine implements Printable, Scannable {
@Override
public void work() {
System.out.println("MFP is working...");
}
@Override
public void print() {
System.out.println("MFP is printing...");
}
@Override
public void scan() {
System.out.println("MFP is scanning...");
}
}
When to choose what?
- If you’re designing base functionality with shared state (for example, fields), use an abstract class.
- If you want to tag objects with “capability labels” (for example, “can print”, “can be compared”, “can be serialized”) — use interfaces.
- If you’re not sure — start with an interface. In Java, that’s considered good practice: interfaces offer greater flexibility and extensibility.
8. Common mistakes and pitfalls
Error No. 1: Trying to extend multiple classes — Java won’t allow it!
A class can extend only one class but implement many interfaces. For example, class A extends B, C — error, whereas class A extends B implements X, Y, Z — perfectly fine.
Error No. 2: Confusing interface fields and class fields.
In an interface, you can declare only constants (public static final). You cannot declare regular state, for example, private int count; — the compiler will stop you immediately.
Error No. 3: Not implementing all interface methods.
If a class fails to implement even one method of an interface, it must be declared abstract; otherwise, the compiler will issue an error.
Error No. 4: Trying to instantiate an interface or an abstract class.
Both of these types are building blocks. You can extend them, but you cannot instantiate them directly:
Printable p = new Printable(); // Error!
Machine m = new Machine(); // Error!
Error No. 5: Thinking an interface can have a constructor.
Interfaces cannot have constructors because they do not describe object state. Only classes (concrete and abstract) can.
GO TO FULL VERSION