Back to Anonymous Classes
Q1Question 1 of 6

Anonymous classes: what they are, when to use them, their restrictions, and how they compare to lambdas

Key Concepts
  • An anonymous class is a nameless, one-shot class declared and instantiated inline with `new Type() { body }`.
  • It can extend exactly one class or implement exactly one interface, never both at the same time.
  • It cannot declare an explicit constructor because it has no name to attach one to.
  • It cannot declare static members other than compile-time constant `static final` fields.
  • Captured local variables must be effectively final because the anonymous instance holds a copy.
  • A lambda targets a single-abstract-method functional interface; an anonymous class handles multi-method interfaces or class extension.
  • Inside an anonymous class `this` refers to the anonymous object; inside a lambda `this` refers to the enclosing instance.

Definition

An anonymous class is a nameless, one-shot subclass or interface implementation written inline where it is used, with no constructor and no instance static members.
  • 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 final fields are allowed; static methods and mutable static fields are forbidden.
  • Effectively-final capture – local variables referenced from the anonymous body must be final or effectively final, because they are copied into the instance.
  • Compiled class file – the compiler generates a numbered class file such as Outer$1.class for 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.

JAVA
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.

JAVA
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.

JAVA
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());
    }
}
TEXT
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 final fields are allowed.
  • Effectively-final capture. Local variables referenced inside the anonymous body must never be reassigned after initialization.
JAVA
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")));
    }
}
TEXT
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) |

JAVA
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));
    }
}
TEXT
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.

JAVA
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.

JAVA
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.

JAVA
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.

JAVA
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

DIAGRAM
┌─────────────────────────────────────────────┐
│         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.

---

Share
Use ← → arrow keys to navigate