Abstract class vs interface in Java
8 min readUpdated
Both let you describe what a type does without saying exactly how, and both cannot be created with new. But they solve different problems. This guide shows the differences side by side and gives you a simple rule for choosing.
The short answer
- Use an interface for a capability many unrelated classes can have:
Comparable,Runnable,PaymentMethod. - Use an abstract class for a shared base of closely related classes that share fields and code:
AccountforSavingsAccountandCurrentAccount. - When unsure, start with an interface. A class can implement many interfaces but extend only one class.
Comparison table
| Abstract class | Interface | |
|---|---|---|
| Keyword | abstract class, used with extends | interface, used with implements |
| How many per class | One | Many |
| Fields | Any fields, including instance state | Only public static final constants |
| Constructors | Yes (run by subclasses) | No |
| Methods | Abstract and concrete, any access level | Abstract, default, static and private (Java 9+) |
| Default access of methods | Package-private | public |
| Relationship | is-a (a SavingsAccount is an Account) | can-do (a Report can be Printable) |
| Lambdas | No | Yes, if it has one abstract method |
An abstract class: shared state and code
An abstract class is right when subclasses share fields and real code, and only some steps differ. Here every account has an id and a balance and the same withdraw rules; only the withdrawal limit is different.
public class AbstractAccounts {
static abstract class Account {
private final String id;
protected double balance;
Account(String id, double balance) {
this.id = id;
this.balance = balance;
}
abstract double withdrawLimit(); // each kind of account decides
boolean withdraw(double amount) { // shared logic, written once
if (amount > balance + withdrawLimit()) return false;
balance -= amount;
return true;
}
String summary() { return id + ": " + balance; }
}
static class SavingsAccount extends Account {
SavingsAccount(String id, double balance) { super(id, balance); }
double withdrawLimit() { return 0; }
}
static class CurrentAccount extends Account {
CurrentAccount(String id, double balance) { super(id, balance); }
double withdrawLimit() { return 5000; } // overdraft allowed
}
public static void main(String[] args) {
Account s = new SavingsAccount("S-1", 1000);
Account c = new CurrentAccount("C-1", 1000);
System.out.println(s.withdraw(3000) + " " + s.summary());
System.out.println(c.withdraw(3000) + " " + c.summary());
}
}Output
false S-1: 1000.0 true C-1: -2000.0
An interface: a capability
An interface is right when very different classes should be usable in the same way. A card, UPI and a wallet have nothing else in common, but all of them can pay.
import java.util.List;
public class PaymentInterfaces {
interface PaymentMethod {
String pay(double amount);
default String receipt(double amount) { // Java 8+: shared default code
return "Receipt: " + pay(amount);
}
}
static class Card implements PaymentMethod {
public String pay(double amount) { return "card charged " + amount; }
}
static class Upi implements PaymentMethod {
public String pay(double amount) { return "UPI request for " + amount; }
}
public static void main(String[] args) {
PaymentMethod wallet = amount -> "wallet debited " + amount; // a lambda works too
for (PaymentMethod m : List.of(new Card(), new Upi(), wallet)) {
System.out.println(m.receipt(250));
}
}
}Output
Receipt: card charged 250.0 Receipt: UPI request for 250.0 Receipt: wallet debited 250.0
What changed in Java 8 and later
Interfaces used to contain only abstract methods. Java 8 added default and static methods, and Java 9 added private methods. Interfaces can now share code, but they still cannot hold instance fields or constructors, so state still belongs in classes.
If a class implements two interfaces with the same default method, it must override that method and can pick one with InterfaceName.super.method().
Using both together
Many libraries combine them: an interface defines the type, an abstract class implements the boring parts, and concrete classes fill in the rest. List → AbstractList → ArrayList is the classic example.
Common mistakes
- Using an abstract class only to share constants or helpers. Use a
finalutility class or an interface constant instead. - Putting instance fields in an interface. They become
public static finalconstants shared by everyone. - Building deep abstract-class hierarchies. Prefer small interfaces plus composition.
- Forgetting that interface methods are
public: an implementing method with weaker access does not compile.
Frequently asked questions
Can an abstract class have a constructor?
Yes. You cannot call new on it, but its constructor runs when a subclass object is created, to set up the fields it declares.
Can an interface have a method with a body?
Yes, since Java 8: default and static methods, and since Java 9 private methods. Abstract methods still have no body.
Can an abstract class implement an interface?
Yes, and it does not have to implement all the interface's methods. The first concrete subclass must implement whatever is left.
Can an abstract class have no abstract methods?
Yes. Marking a class abstract simply prevents creating objects of it directly.
Learn it step by step
Practise this, step by step
Short lessons with quizzes, practice and interview questions, from your first program to job-ready.