Definition
Since Java 8, interfaces have narrowed the gap with abstract classes. The remaining differences drive the choice.
- State: abstract class can have instance fields (mutable state). Interface can only have
public static finalconstants. - Constructors: abstract class can have constructors that initialize shared state. Interface cannot.
- Single vs multiple: a class can extend only one abstract class but implement multiple interfaces.
- Access modifiers: abstract class methods can be any access level. Interface methods are implicitly
public(private since Java 9 for helpers). - Default methods (Java 8): interfaces can have method bodies, but they still cannot have instance fields or constructors.
- Choose interface when defining a capability contract that unrelated classes can implement. Choose abstract class when sharing mutable state and constructors among a family of related classes.
An interface is like a banking license: any institution that implements DepositService must provide deposit() and withdraw(). A bank, a payment app, and a cooperative can all hold this license. Multiple licenses allowed. An abstract class is like a bank template at National Bank of India: all branches share common code (balance tracking, audit logging), but each branch fills in its own interest rate. Only one template per branch (single inheritance). The template has state (balance field) and a constructor (initializes accountId).
Understanding the Concepts
What is the difference between abstract class and interface?
Since Java 8, the feature gap has narrowed significantly. But four key differences remain.
What are the four remaining differences?
package com.bank.core;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
// Difference 1: CONSTRUCTORS (abstract class: yes, interface: no)
public abstract class Account {
private final String accountId;
protected BigDecimal balance;
protected Account(String accountId, BigDecimal initialBalance) {
if (accountId == null) throw new IllegalArgumentException("required");
this.accountId = accountId;
this.balance = initialBalance;
}
// Interface: no constructor. Cannot initialize state on creation.
}
// Difference 2: INSTANCE FIELDS (abstract class: yes, interface: no)
// Account has: protected BigDecimal balance (mutable instance field)
// Interface has: BigDecimal MAX = new BigDecimal("10000000"); // public static final only
// Difference 3: ACCESS MODIFIERS (abstract class: any, interface: public only)
// Account can have: protected void internalValidate() { }
// Interface: all methods implicitly public (private since Java 9 for helpers)
// Difference 4: SINGLE vs MULTIPLE INHERITANCE
// class SavingsAccount extends Account implements Auditable, Notifiable, Taxable { }
// Cannot: class X extends Account, AnotherClass { } // compile error
How does an interface look in Java 8+?
public interface PaymentChannel {
// Abstract method: implementor must provide body
TransferResult transfer(String from, String to, BigDecimal amount);
// Default method (Java 8): has body, inherited by implementors
default boolean isAvailable() {
return true; // implementors can override
}
// Static method (Java 8): utility, called via InterfaceName.method()
static PaymentChannel defaultChannel() {
return new NeftChannel();
}
// Private method (Java 9): helper for default methods
private void logTransfer(String msg) {
System.out.println("[PaymentChannel] " + msg);
}
// Constants only: no instance fields
BigDecimal MAX_TRANSFER = new BigDecimal("10000000"); // implicitly public static final
}
How does an abstract class look?
public abstract class Account {
private final String accountId;
protected BigDecimal balance;
private final List<String> auditLog = new ArrayList<>();
protected Account(String accountId, BigDecimal initialBalance) {
this.accountId = accountId;
this.balance = initialBalance;
auditLog.add("OPENED: Rs." + initialBalance);
}
public void deposit(BigDecimal amount) {
balance = balance.add(amount);
auditLog.add("CREDIT: Rs." + amount);
}
public abstract BigDecimal calculateInterest(); // subclass must implement
// Template Method Pattern: concrete method calling abstract method
public void processMonthEnd() {
BigDecimal interest = calculateInterest();
if (interest.signum() > 0) {
deposit(interest);
}
}
}
When does the choice actually matter?
| Decision Factor | Choose Interface | Choose Abstract Class |
|----------------|-----------------|----------------------|
| Need instance fields? | No | Yes |
| Need constructor? | No | Yes |
| Multiple unrelated classes? | Yes (multiple interfaces) | No (single extends) |
| Defining a capability? | Yes (Auditable, Comparable) | No |
| Defining a family hierarchy? | No | Yes (Account hierarchy) |
| Template Method Pattern? | No | Yes |
| Default choice? | Yes (prefer interface) | Only when state needed |
What does a real-world example look like?
// Account: abstract class (has state, constructor, template method)
public abstract class Account {
protected BigDecimal balance;
protected Account(BigDecimal balance) { this.balance = balance; }
public abstract BigDecimal calculateInterest();
}
// Auditable: interface (capability, any class can have it)
public interface Auditable {
List<String> generateAuditReport();
}
// Taxable: interface (capability, any class can have it)
public interface Taxable {
BigDecimal calculateTax();
}
// SavingsAccount: uses both
public class SavingsAccount extends Account implements Auditable, Taxable {
// Extends ONE abstract class (Account: shared state + template).
// Implements MULTIPLE interfaces (Auditable + Taxable: capabilities).
}
What are common mistakes?
The most common mistake is using an abstract class when an interface would suffice, wasting the single inheritance slot.
// WRONG: wasting inheritance on a contract with no state
public abstract class Auditable {
public abstract List<String> generateReport();
}
// Now SavingsAccount extends Account but cannot also extend Auditable.
// CORRECT: use interface for stateless contracts
public interface Auditable {
List<String> generateReport();
}
// SavingsAccount extends Account implements Auditable. Both slots used well.
What should a senior engineer know?
- In Spring, services implement interfaces for loose coupling. JpaRepository is an interface (multiple implementations possible).
- The Template Method Pattern is the primary use case for abstract classes in modern Java.
- Records (Java 16) can implement interfaces but cannot extend classes (they already extend Record).
- Sealed interfaces (Java 17) control which classes can implement them, enabling exhaustive pattern matching.
Visual Overview
ABSTRACT CLASS vs INTERFACE
──────────────────────────────────────────
Abstract Class Interface
┌──────────────────┐ ┌──────────────────┐
│ Instance fields │ │ Constants only │
│ Constructors │ │ No constructors │
│ Any access level │ │ public only │
│ Single extends │ │ Multiple implements│
│ Template Method │ │ Default methods │
└──────────────────┘ └──────────────────┘
class SavingsAccount
extends Account <- single abstract class
implements Auditable, <- multiple interfaces
Notifiable,
Taxable
Key Takeaway
Since Java 8, the feature gap between abstract class and interface has narrowed: interfaces now have default methods, static methods, and private methods. The remaining differences drive the choice: abstract classes can have instance fields and constructors (shared mutable state); interfaces cannot. A class can extend only one abstract class but implement multiple interfaces. Use abstract class when you need shared state across subclasses (balance field, audit log) and a constructor to initialize it. Use interface when you define a capability (Auditable, Serializable, Comparable) that multiple unrelated classes may need.
In Pankaj's banking platform at National Bank of India, Account is an abstract class because it has shared mutable state (balance, auditLog), a constructor, and a template method (processMonthEnd calls calculateInterest and deposit). Auditable, Notifiable, and Taxable are interfaces because SavingsAccount needs two of them, CurrentAccount needs all three, and the single abstract class inheritance slot is already used by Account.
Remember one sentence: prefer interfaces with default methods as the default choice, and use abstract classes only when you need constructors or shared mutable instance fields.
---