Definition
- Anonymous class – a local class with no name, declared and instantiated in a single
new Type() { ... }expression. - Single base type – it extends exactly one class or implements exactly one interface, never both, and never more than one interface.
- No explicit constructor – because the class has no name, you cannot write a constructor; use an instance initializer
{{ }}block instead. - No static members – only compile-time constant
static finalfields are allowed; static methods and mutable static fields are forbidden. - Effectively-final capture – local variables referenced from the anonymous body must be
finalor effectively final, because they are copied into the instance. - Compiled class file – the compiler generates a numbered class file such as
Outer$1.classfor each anonymous class.
Imagine Rajesh walks into National Bank of India and needs a one-time signatory for a single cheque. Instead of hiring a permanent, named employee for that one task, the branch manager grabs any available person, hands them a form with the exact instructions written on it, and that person signs just this once and is never seen again. There is no need to give that temporary helper a name, a job title, or a desk. That throwaway, nameless, one-task helper is an anonymous class.
Understanding the Concepts
What is an anonymous class?
An anonymous class is a local class without a name that you declare and instantiate in one expression. You write new SuperType(args) { body }, and the compiler creates a hidden subclass, instantiates it, and hands you the reference. The class exists only at that point in the code and cannot be reused elsewhere by name.
package com.bank.service;
import java.math.BigDecimal;
import java.util.Comparator;
import java.util.List;
public class TradeService {
public void sortTradesByAmount(List<Trade> trades) {
// Anonymous class implementing Comparator
trades.sort(new Comparator<Trade>() {
@Override
public int compare(Trade a, Trade b) {
return a.getAmount().compareTo(b.getAmount());
}
});
}
}
The object is created immediately. The class has no name you can refer to anywhere else in the code.
Why do we need anonymous classes?
Anonymous classes exist for situations where you need a one-off implementation that is too small to justify a named class file. Before Java 8 lambdas, every callback, listener, and comparator was written as an anonymous class. Even after lambdas arrived, anonymous classes remain necessary when the target interface has more than one abstract method, or when you need to extend a concrete class rather than implement a functional interface.
package com.bank.service;
import java.math.BigDecimal;
public class TransferService {
interface TransferListener {
void onStart(String fromAccount, String toAccount);
void onComplete(BigDecimal amount);
}
public void executeTransfer(String from, String to, BigDecimal amount) {
// Two abstract methods: lambda cannot implement this
TransferListener listener = new TransferListener() {
@Override
public void onStart(String fromAccount, String toAccount) {
System.out.println("Transfer started: " + fromAccount + " -> " + toAccount);
}
@Override
public void onComplete(BigDecimal transferredAmount) {
System.out.println("Transfer complete: " + transferredAmount + " INR");
}
};
listener.onStart(from, to);
// ... perform transfer logic ...
listener.onComplete(amount);
}
}
How do you create an anonymous class?
The syntax is new SuperType(constructorArgs) { body }. When SuperType is a class, the constructor arguments go to the superclass constructor. When SuperType is an interface, the argument list must be empty because interfaces have no constructors.
package com.bank.model;
import java.math.BigDecimal;
public class AccountDemo {
static abstract class Account {
String id;
BigDecimal balance;
Account(String id, BigDecimal balance) {
this.id = id;
this.balance = balance;
}
abstract BigDecimal monthlyFee();
}
public static void main(String[] args) {
// Anonymous class extending a class with constructor args
Account priyaAccount = new Account("SAV-1001", new BigDecimal("500000")) {
@Override
BigDecimal monthlyFee() {
return new BigDecimal("150.00");
}
};
System.out.println("Account: " + priyaAccount.id);
System.out.println("Monthly fee: " + priyaAccount.monthlyFee());
}
}
Account: SAV-1001
Monthly fee: 150.00
What are the restrictions of anonymous classes?
Anonymous classes have four key restrictions.
- No explicit constructor. The class has no name, so you cannot write a constructor. Use an instance initializer block for setup logic.
- Single base type. You can extend one class or implement one interface, never both, and never multiple interfaces.
- No static members. You cannot declare static methods or mutable static fields. Only compile-time constant
static finalfields are allowed. - Effectively-final capture. Local variables referenced inside the anonymous body must never be reassigned after initialization.
package com.bank.service;
import java.math.BigDecimal;
public class RestrictionDemo {
interface FeeCalculator {
BigDecimal calculate(BigDecimal principal);
}
public void demo() {
BigDecimal rate = new BigDecimal("0.02"); // effectively final
FeeCalculator calc = new FeeCalculator() {
// Instance initializer instead of constructor
{
System.out.println("FeeCalculator initialized");
}
// Allowed: compile-time constant
static final String TYPE = "PROCESSING_FEE";
// Not allowed: static method
// static void helper() { } // compile error
@Override
public BigDecimal calculate(BigDecimal principal) {
return principal.multiply(rate); // captures effectively-final rate
}
};
System.out.println(calc.calculate(new BigDecimal("100000")));
}
}
FeeCalculator initialized
2000.00
How does an anonymous class compare to a lambda?
The core difference is that a lambda can only implement a functional interface (one abstract method), whereas an anonymous class can implement multi-method interfaces or extend a concrete class.
| Feature | Anonymous Class | Lambda |
|---|---|---|
| Target | Any class or interface | Functional interface only |
| this keyword | Refers to anonymous object | Refers to enclosing instance |
| Constructor | No (use initializer block) | No |
| State (fields) | Can declare instance fields | No fields |
| Static members | Only constant static final | No |
| Bytecode | Generates Outer$1.class | Uses invokedynamic |
| Memory | Heavier (new class per site) | Lighter (shared at runtime) |
package com.bank.service;
import java.math.BigDecimal;
import java.util.function.Function;
public class LambdaVsAnonymous {
public static void main(String[] args) {
BigDecimal balance = new BigDecimal("250000");
// Lambda: functional interface with one method
Function<BigDecimal, BigDecimal> lambdaFee = amount ->
amount.multiply(new BigDecimal("0.01"));
// Anonymous class: same result but more verbose
Function<BigDecimal, BigDecimal> anonFee = new Function<BigDecimal, BigDecimal>() {
@Override
public BigDecimal apply(BigDecimal amount) {
return amount.multiply(new BigDecimal("0.01"));
}
};
System.out.println("Lambda fee: " + lambdaFee.apply(balance));
System.out.println("Anonymous fee: " + anonFee.apply(balance));
}
}
Lambda fee: 2500.00
Anonymous fee: 2500.00
What are common mistakes with anonymous classes?
The most frequent mistake is trying to reassign a captured local variable, which breaks the effectively-final rule.
package com.bank.service;
import java.math.BigDecimal;
public class CommonMistakes {
interface ResultCallback {
void onResult(BigDecimal amount);
}
// WRONG: reassigning captured variable
public void wrongApproach() {
BigDecimal total = BigDecimal.ZERO;
ResultCallback cb = new ResultCallback() {
@Override
public void onResult(BigDecimal amount) {
// total = total.add(amount); // COMPILE ERROR
}
};
total = new BigDecimal("100"); // reassignment makes it non-effectively-final
}
// CORRECT: use a mutable holder
public void correctApproach() {
BigDecimal[] total = { BigDecimal.ZERO }; // array reference is effectively final
ResultCallback cb = new ResultCallback() {
@Override
public void onResult(BigDecimal amount) {
total[0] = total[0].add(amount); // mutate array contents
}
};
cb.onResult(new BigDecimal("50000"));
System.out.println("Total: " + total[0]);
}
}
When should you use an anonymous class?
Use an anonymous class when you need a one-off implementation of a multi-method interface or must extend a concrete class inline. If the target is a functional interface with a single abstract method, prefer a lambda because it is shorter, clearer, and lighter at runtime.
package com.bank.service;
import java.math.BigDecimal;
public class UsageGuideline {
// Multi-method interface: anonymous class is the right tool
interface AuditCallback {
void beforeAudit(String accountId);
void afterAudit(String accountId, BigDecimal auditedBalance);
}
public void auditAccount(String accountId) {
AuditCallback callback = new AuditCallback() {
@Override
public void beforeAudit(String id) {
System.out.println("Starting audit for " + id);
}
@Override
public void afterAudit(String id, BigDecimal balance) {
System.out.println("Audit complete for " + id + ": " + balance + " INR");
}
};
callback.beforeAudit(accountId);
callback.afterAudit(accountId, new BigDecimal("1500000"));
}
}
What about thread safety and concurrency?
An anonymous class that captures effectively-final variables holds a stable snapshot, which is inherently safe to pass to another thread. If you use a mutable holder like AtomicReference for shared state, you must handle thread safety yourself.
package com.bank.service;
import java.math.BigDecimal;
import java.util.concurrent.atomic.AtomicReference;
public class ThreadSafeAnonymous {
public void processBatchTransfers() {
AtomicReference<BigDecimal> totalTransferred =
new AtomicReference<>(BigDecimal.ZERO);
Runnable task = new Runnable() {
@Override
public void run() {
BigDecimal amount = new BigDecimal("25000");
totalTransferred.updateAndGet(t -> t.add(amount));
System.out.println(Thread.currentThread().getName()
+ " transferred " + amount + " INR");
}
};
Thread t1 = new Thread(task, "Worker-1");
Thread t2 = new Thread(task, "Worker-2");
t1.start();
t2.start();
}
}
What about serialization?
Anonymous classes can implement Serializable, but serialization captures the enclosing instance and all captured variables. If the enclosing class is not serializable, you get a NotSerializableException. In banking systems, prefer named classes for anything that crosses a serialization boundary.
package com.bank.service;
import java.io.Serializable;
import java.math.BigDecimal;
public class SerializationWarning {
// This anonymous class captures the enclosing SerializationWarning instance
Serializable feeRule = new Serializable() {
private static final long serialVersionUID = 1L;
BigDecimal rate = new BigDecimal("0.015");
@Override
public String toString() {
return "FeeRule{rate=" + rate + "}";
}
};
// If SerializationWarning is not Serializable, writing feeRule
// to an ObjectOutputStream throws NotSerializableException
}
Visual Overview
┌─────────────────────────────────────────────┐
│ Anonymous Class Creation Flow │
├─────────────────────────────────────────────┤
│ │
│ new InterfaceOrClass(args) { │
│ // body with overrides │
│ }; │
│ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ Compiler generates │ │
│ │ Outer$1.class │ │
│ │ (extends/implements │ │
│ │ the single base type) │ │
│ └───────────┬─────────────┘ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ Instance created on │ │
│ │ the heap immediately │ │
│ │ Captured locals copied │ │
│ │ into hidden fields │ │
│ └─────────────────────────┘ │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ Anonymous Class vs Lambda Decision │
├─────────────────────────────────────────────┤
│ │
│ Is the target a functional interface? │
│ │ │
│ ├── YES ──→ Use a lambda │
│ │ │
│ └── NO ───→ Is it multi-method │
│ interface or class? │
│ │ │
│ └── YES ──→ Use │
│ anonymous class│
└─────────────────────────────────────────────┘
Key Takeaway
An anonymous class is a local class without a name that you declare and instantiate in one expression. It extends exactly one class or implements exactly one interface, with the implementation written inline at the point of use. Its defining restrictions are that it cannot declare an explicit constructor because it has no name, it cannot declare static members other than compile-time constants, and it may capture only effectively-final local variables from the enclosing method.
In a banking project at National Bank of India, anonymous classes appear wherever Rajesh needs a one-off listener or callback with more than one method. A transfer listener with onStart and onComplete cannot be a lambda because it has two abstract methods, so an anonymous class supplies both methods inline. For single-method callbacks like a result handler, a lambda is shorter and clearer.
The comparison with lambdas is the part interviewers focus on. A lambda can only target a functional interface, meaning one abstract method, whereas an anonymous class can implement a multi-method interface or extend a concrete class. Inside an anonymous class this refers to the anonymous object, but inside a lambda this refers to the enclosing instance, which matters when you need the implementation object's own identity. Lambdas are also lighter at runtime because the compiler uses invokedynamic rather than always generating a separate class file.
The practical rule is straightforward. If the target is a functional interface and you just need a small piece of behaviour, write a lambda. If the interface has multiple methods, if you must extend a class, or if you need the implementation object's own this, write an anonymous class. This keeps the code concise where it can be and correct where it must be.
Remember one sentence: an anonymous class is a nameless, one-shot subclass or interface implementation declared inline, restricted to a single base type with no constructor and no static members, and you prefer a lambda whenever the target is a functional interface.
---