Definition
You cannot instantiate an abstract class directly because it has unimplemented methods.
- Direct instantiation:
new Account()on an abstract Account is a compile error. - Anonymous subclass:
new Account("id", balance) { @Override calculateInterest() { return ZERO; } }creates an unnamed class that extends Account, implements all abstract methods, and is instantiated in one expression. - Lambda (Java 8): replaced anonymous classes for functional interfaces (one abstract method).
(a, b) -> a.compareTo(b)is shorter thannew Comparator<>() { @Override int compare(...) { ... } }. - Lambda limitation: lambdas cannot replace anonymous subclasses for abstract classes (which have constructors and fields) or interfaces with multiple abstract methods.
- Anonymous class features: has its own
thisreference, can have instance fields, generates a separate.classfile.
An abstract class is like a blank form template at National Bank of India. You cannot submit a blank form (cannot instantiate). You must fill in the blanks (implement abstract methods). Anonymous subclass means filling in the blanks right there on the spot. Named subclass means taking the form home (creating a separate class), filling it in, and bringing it back later. Both produce a valid, complete form.
Understanding the Concepts
Can you instantiate an abstract class?
No. But you can create an anonymous subclass that implements all abstract methods inline.
What happens when you try to instantiate directly?
package com.bank.core;
import java.math.BigDecimal;
public abstract class Account {
private final String accountId;
protected BigDecimal balance;
protected Account(String accountId, BigDecimal balance) {
this.accountId = accountId;
this.balance = balance;
}
public abstract BigDecimal calculateInterest();
public abstract String getType();
}
// Compile error:
// Account account = new Account("SAV001", BigDecimal.ZERO);
// "Cannot instantiate the type Account"
How do you create an anonymous subclass?
// Named subclass (separate class file):
public class SavingsAccount extends Account {
public SavingsAccount(String id, BigDecimal bal) { super(id, bal); }
@Override public BigDecimal calculateInterest() { return balance.multiply(new BigDecimal("0.04")); }
@Override public String getType() { return "SAVINGS"; }
}
Account named = new SavingsAccount("SAV001", new BigDecimal("125000"));
// Anonymous subclass (inline, no separate class file):
Account anonymous = new Account("ANON001", new BigDecimal("50000")) {
@Override
public BigDecimal calculateInterest() {
return getBalance().multiply(new BigDecimal("0.03"));
}
@Override
public String getType() {
return "ANONYMOUS";
}
};
// anonymous.getClass().getSimpleName() -> "" (empty: truly anonymous)
// anonymous.getClass().getName() -> "com.bank.Main$1" (compiler-generated)
When did lambda replace anonymous classes?
// Before Java 8: anonymous class for Comparator
java.util.Comparator<Trade> oldWay = new java.util.Comparator<Trade>() {
@Override
public int compare(Trade a, Trade b) {
return a.getAmount().compareTo(b.getAmount());
}
};
// After Java 8: lambda for functional interface (one abstract method)
java.util.Comparator<Trade> newWay = (a, b) -> a.getAmount().compareTo(b.getAmount());
// Lambda does NOT work for abstract classes:
// Account lambda = (id, bal) -> { ... }; // compile error: not a functional interface
When are anonymous subclasses still useful?
// 1. Abstract class: lambda does not work
Account testAccount = new Account("TEST", BigDecimal.ZERO) {
@Override public BigDecimal calculateInterest() { return BigDecimal.ZERO; }
@Override public String getType() { return "TEST"; }
};
// Useful in tests: quick one-off implementation without a full class file.
// 2. Interface with multiple abstract methods: lambda does not work
interface TradeValidator {
boolean validateAmount(BigDecimal amount);
boolean validateSymbol(String symbol);
}
TradeValidator validator = new TradeValidator() {
@Override public boolean validateAmount(BigDecimal amount) { return amount.signum() > 0; }
@Override public boolean validateSymbol(String symbol) { return symbol != null; }
};
// 3. Need instance state inside the anonymous class
java.util.Comparator<Trade> withState = new java.util.Comparator<Trade>() {
private int compareCount = 0; // state: lambda cannot have instance fields
@Override public int compare(Trade a, Trade b) {
compareCount++;
return a.getAmount().compareTo(b.getAmount());
}
};
How do anonymous classes compare to lambdas?
| Aspect | Anonymous Class | Lambda |
|--------|----------------|--------|
| Works for | Any interface or abstract class | Functional interface only (one abstract method) |
| this keyword | Refers to anonymous class instance | Refers to enclosing class |
| Instance fields | Can have | Cannot |
| .class file | Generates separate file (Main$1.class) | Uses invokedynamic (no .class file) |
| Verbosity | 5+ lines | 1 line |
What are common mistakes?
The most common mistake is trying to use a lambda for an abstract class or a multi-method interface.
// WRONG: lambda for abstract class
// Account a = () -> BigDecimal.ZERO; // compile error
// WRONG: lambda for multi-method interface
// TradeValidator v = (amount) -> amount.signum() > 0; // compile error: two abstract methods
What should a senior engineer know?
- Anonymous subclasses are used extensively in test fixtures: quick one-off implementations of abstract classes without creating dedicated test doubles.
- In Spring, anonymous implementations are used in configuration classes for customizing beans inline.
- Records cannot be anonymous because they are implicitly final.
Visual Overview
ABSTRACT CLASS INSTANTIATION
──────────────────────────────────────────
new Account() -> COMPILE ERROR
(cannot instantiate abstract)
new Account() { -> ANONYMOUS SUBCLASS
@Override methods() (compiler generates Main$1.class)
};
new SavingsAccount() -> NAMED SUBCLASS
(separate .java file)
LAMBDA vs ANONYMOUS
──────────────────────────────────────────
Functional interface (1 method) -> use lambda
Abstract class -> must use anonymous
Interface (2+ methods) -> must use anonymous
Need instance fields -> must use anonymous
Key Takeaway
You cannot instantiate an abstract class directly: new Account() is a compile error because abstract classes have unimplemented methods. But you can create an anonymous subclass that provides implementations inline: new Account(id, balance) { @Override calculateInterest() { ... } }. The compiler generates an unnamed class that extends the abstract class, implements all abstract methods, and is instantiated immediately.
Before Java 8, anonymous classes were the primary way to provide on-the-fly implementations. Lambdas (Java 8) replaced this for functional interfaces (one abstract method). But lambdas cannot replace anonymous subclasses for abstract classes (which have constructors, fields, and multiple abstract methods) or interfaces with multiple abstract methods. Anonymous classes are still used in test fixtures, event handlers, and configuration customization.
Remember one sentence: you cannot instantiate an abstract class, but you can create an anonymous subclass inline, and lambdas only replace anonymous classes for functional interfaces with one abstract method.
---