Definition
An abstract class can simultaneously extend one other class and implement multiple interfaces.
- Extends one class:
abstract class InterestBearingAccount extends Accountinherits state and behavior from Account. - Implements multiple interfaces:
implements Auditable, Taxabledeclares capabilities. - Can leave methods unimplemented: being abstract, it can defer interface method implementations to concrete subclasses.
- Concrete subclass must finish: a non-abstract class at the bottom of the chain must implement all remaining abstract methods from the parent chain and all unimplemented interface methods.
- Creates layered hierarchy: each layer adds more specificity. Account (base state) -> InterestBearingAccount (adds interest + declares Auditable) -> SavingsAccount (provides all implementations).
Think of it as a training manual at National Bank of India. Account (abstract class) is the master manual with common procedures (deposit, withdraw). InterestBearingAccount (abstract class) extends Account and adds interest-related procedures but leaves calculateInterest() for specific account types to define. It also implements Auditable and Taxable, declaring those capabilities. SavingsAccount (concrete class) extends InterestBearingAccount and provides all the missing pieces.
Understanding the Concepts
Can an abstract class extend another class and implement interfaces?
Yes. The key insight: an abstract class is allowed to have unfinished business. It can declare that it implements an interface without providing the method body.
How does a layered hierarchy work?
package com.bank.core;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
// Layer 1: base abstract class with state
public abstract class Account {
protected String accountId;
protected BigDecimal balance;
protected Account(String id, BigDecimal bal) {
this.accountId = id;
this.balance = bal;
}
public void deposit(BigDecimal amount) { balance = balance.add(amount); }
public abstract String getAccountType();
}
// Layer 2: abstract class extends Account AND implements 2 interfaces
public abstract class InterestBearingAccount extends Account implements Auditable, Taxable {
protected BigDecimal annualRate;
protected InterestBearingAccount(String id, BigDecimal bal, BigDecimal rate) {
super(id, bal);
this.annualRate = rate;
}
public BigDecimal calculateMonthlyInterest() {
return balance.multiply(annualRate).divide(BigDecimal.valueOf(12), 2, RoundingMode.HALF_UP);
}
// getAccountType() from Account: still abstract, passed to concrete subclass.
// Auditable.generateAuditReport(): still abstract, passed to concrete subclass.
// Taxable.calculateTax(): still abstract, passed to concrete subclass.
// This class must be abstract because it has unimplemented methods.
}
// Layer 3: concrete class implements ALL remaining abstract methods
public class SavingsAccount extends InterestBearingAccount {
public SavingsAccount(String id, BigDecimal bal, BigDecimal rate) {
super(id, bal, rate);
}
@Override
public String getAccountType() { return "SAVINGS"; }
@Override
public List<String> generateAuditReport() {
return List.of("Account: " + accountId, "Balance: Rs." + balance);
}
@Override
public BigDecimal calculateTax() {
BigDecimal yearlyInterest = calculateMonthlyInterest().multiply(BigDecimal.valueOf(12));
return yearlyInterest.multiply(new BigDecimal("0.30")); // 30% tax on interest
}
}
What methods are abstract at each layer?
| Layer | Class | Abstract Methods | Concrete Methods |
|-------|-------|-----------------|------------------|
| 1 | Account | getAccountType() | deposit() |
| 2 | InterestBearingAccount | getAccountType(), generateAuditReport(), calculateTax() | calculateMonthlyInterest() |
| 3 | SavingsAccount | None (all implemented) | Everything above + overrides |
Can the middle layer partially implement interface methods?
Yes. The abstract class can implement some interface methods and leave others for the concrete subclass.
public abstract class InterestBearingAccount extends Account implements Auditable, Taxable {
// Implements Auditable.generateAuditReport() here:
@Override
public List<String> generateAuditReport() {
return List.of("Account: " + accountId, "Type: " + getAccountType());
}
// Leaves Taxable.calculateTax() abstract: concrete subclass must implement.
}
// Now SavingsAccount only needs: getAccountType() + calculateTax()
When is this pattern useful?
This pattern is useful when multiple concrete classes share intermediate behavior but differ in specifics.
// FixedDepositAccount also extends InterestBearingAccount:
public class FixedDepositAccount extends InterestBearingAccount {
@Override public String getAccountType() { return "FD"; }
@Override public List<String> generateAuditReport() { /* FD-specific */ }
@Override public BigDecimal calculateTax() { /* different tax rules for FD */ }
}
// Both SavingsAccount and FixedDepositAccount share:
// - Account state (accountId, balance, deposit)
// - InterestBearingAccount behavior (calculateMonthlyInterest)
// - Auditable and Taxable capabilities
// But each provides its own getAccountType(), audit report, and tax calculation.
What are common mistakes?
The most common mistake is forgetting that the concrete subclass must implement all remaining abstract methods.
// WRONG: concrete class missing abstract method implementation
// public class SavingsAccount extends InterestBearingAccount {
// @Override public String getAccountType() { return "SAVINGS"; }
// // Missing: generateAuditReport() and calculateTax()
// // Compile error: "SavingsAccount is not abstract and does not override..."
// }
What should a senior engineer know?
- This layered pattern maps directly to Domain-Driven Design aggregate hierarchies. - In JPA, a base entity class can be abstract with @MappedSuperclass, extended by domain-specific abstract classes, and finally by concrete entities. - In Spring, this pattern is used in AbstractController -> AbstractAnnotationConfigDispatcherServletInitializer hierarchies.
Visual Overview
LAYERED HIERARCHY
──────────────────────────────────────────
Layer 1: Account (abstract)
┌─────────────────────────────────┐
│ fields: accountId, balance │
│ concrete: deposit() │
│ abstract: getAccountType() │
└──────────────┬──────────────────┘
│ extends
Layer 2: InterestBearingAccount (abstract)
┌─────────────────────────────────┐
│ implements Auditable, Taxable │
│ fields: annualRate │
│ concrete: calculateMonthlyInterest()│
│ abstract: getAccountType() │
│ abstract: generateAuditReport() │
│ abstract: calculateTax() │
└──────────────┬──────────────────┘
│ extends
Layer 3: SavingsAccount (concrete)
┌─────────────────────────────────┐
│ implements ALL remaining methods │
│ getAccountType() -> "SAVINGS" │
│ generateAuditReport() -> [...] │
│ calculateTax() -> 30% of interest│
└─────────────────────────────────┘
Key Takeaway
Yes, an abstract class can simultaneously extend one other class and implement multiple interfaces. It does not need to implement all interface methods: being abstract, it can leave unimplemented methods for concrete subclasses. This creates a layered hierarchy where each layer adds more specificity: Account (base state) to InterestBearingAccount (adds interest and declares Auditable and Taxable) to SavingsAccount (provides all remaining implementations).
In Pankaj's banking system at National Bank of India, InterestBearingAccount extends Account (inheriting accountId, balance, deposit) and implements Auditable plus Taxable. It provides calculateMonthlyInterest() but leaves calculateTax() and getAccountType() abstract, so it must be declared abstract. SavingsAccount extends InterestBearingAccount and implements both remaining methods, making it concrete. FixedDepositAccount does the same with different tax rules.
Remember one sentence: an abstract class can extend one class and implement multiple interfaces, leaving unimplemented methods for concrete subclasses, which creates a layered hierarchy from general to specific.
---