Back to Blog
Java

Java Anonymous Class: Syntax, Scope, and Practical Use

Learn how to declare and use anonymous classes in Java, including variable capture, scope, and when to prefer lambdas or local classes.

JavaAnonymous ClassesInner ClassesLambda ExpressionsFunctional Interfaces
Diagram showing an anonymous class implementing an interface with a method override in Java.

In Java, an anonymous class is a class declared at the point of instantiation without a named type. It is typically used to provide an immediate implementation of an interface or an extension of a class for a single use. The core pattern is to define and instantiate the class in one expression, as shown by a common use with Comparator:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); Collections.sort(names, new Comparator<String>() { @Override public int compare(String a, String b) { return a.length() - b.length(); } });

The expression new Comparator<String>() { ... } creates an instance of an unnamed class that implements Comparator<String>. The class body contains the implementations required by the interface. This is the essence of the anonymous class pattern: you define and instantiate a class in one step, without a separate class declaration.

The Syntax of an Anonymous Class

An anonymous class is written as new Type() { body }. Type is either an interface or a class. If Type is an interface, the anonymous class implements it. If Type is a class, the anonymous class extends it and can override methods. The body can contain fields, methods, and initializers, but the class has no name, so it cannot declare a constructor. Constructor arguments for a superclass are passed in the parentheses after Type; for an interface, the parentheses are still required by the syntax, but no constructor arguments are passed.

Here is an example that implements Runnable:

new Thread(new Runnable() { @Override public void run() { System.out.println("Running in a thread"); } }).start();

The same syntax works when extending a class such as ArrayList:

ArrayList<String> list = new ArrayList<String>() { @Override public boolean add(String element) { System.out.println("Adding: " + element); return super.add(element); } };

Here the anonymous class extends ArrayList<String> and overrides add to log each insertion. The empty parentheses in the new expression are required and select ArrayList's no-argument constructor.

Capturing Variables and the Effectively Final Rule

An anonymous class can access local variables from the enclosing scope only when those variables are effectively final. A variable is effectively final if it is not reassigned after initialization. The compiler copies such values into the anonymous class instance; allowing later mutation would make that copy stale and inconsistent with the original local variable.

int base = 10; Runnable r = new Runnable() { @Override public void run() { System.out.println(base); // OK, base is effectively final } };

If you later reassign base, the compiler rejects the reference from inside the anonymous class. The same restriction applies to lambda expressions, although lambdas are more concise when the body is simple. The effectively final rule is a common source of confusion, especially when developers expect to modify a counter or accumulator inside a callback.

Using this Inside an Anonymous Class

Inside an anonymous class, this refers to the anonymous class instance, not the enclosing object. This difference matters when you need to access a method or field of the outer class. For example:

public class Outer { private String name = "outer"; void createRunnable() { Runnable r = new Runnable() { @Override public void run() { System.out.println(this.name); // error: no field 'name' in anonymous class } }; } }

To access the outer instance, qualify this with the outer class name: Outer.this.name. This syntax is shared by inner classes and anonymous classes. In a lambda expression, this refers to the enclosing instance, which is one of the behavioral differences between the two constructs.

Anonymous Class vs. Lambda Expression

When the interface has exactly one abstract method (a functional interface), a lambda expression is usually a better fit. Lambdas are more concise and are compiled with invokedynamic rather than a separate class file, starting in Java 8. Lambdas cannot declare fields or instance initializers, and they implement only one abstract method. Anonymous classes can implement interfaces with multiple methods, though that is uncommon and often indicates that a named class would be clearer.

The following table summarizes the key differences:

FeatureAnonymous ClassLambda Expression
Syntaxnew Type() { ... }(args) -> body
this referenceRefers to the anonymous instanceRefers to the enclosing instance
Fields and initializersAllowedNot allowed
Multiple methodsCan implement all interface methodsOnly one abstract method
Class file generationGenerates a separate .class fileUses invokedynamic (Java 8+)
Variable captureEffectively finalEffectively final

Use a lambda when the interface is functional and the logic is short. Use an anonymous class when you need instance-like state (fields) or when the interface has more than one abstract method. In modern Java, lambdas cover most use cases that historically required anonymous classes.

Local and Anonymous Classes: When to Choose a Named Class

Anonymous classes are one kind of nested class. A local class is declared inside a method and has a name; a member class is declared as a member of the enclosing class. The choice among these depends on reusability and readability. If you need the same behavior in several places, a named local or member class avoids duplication. An anonymous class is best for a one-off implementation that is clear at the call site.

For example, a local class can be defined and instantiated multiple times within the same method:

class Greeter { void greet() { class HelloRunnable implements Runnable { @Override public void run() { System.out.println("Hello"); } } Runnable r = new HelloRunnable(); new Thread(r).start(); } }

This is more verbose but gives the class a name for debugging and allows reuse within the method. For production code, if the implementation is complex or likely to change, a named class is easier to test and maintain.

Memory and Runtime Characteristics

Each anonymous class definition generates a separate .class file when compiled. This increases the number of classes loaded at runtime, which can affect startup time and memory footprint in large applications. The impact is usually negligible unless you create many anonymous classes. The instance itself is a normal object; it occupies heap memory and is garbage-collected when no longer referenced.

An anonymous class that references outer fields or methods holds a reference to the enclosing instance. If the anonymous class instance is stored in a long-lived collection while the outer object becomes unreachable, the outer object cannot be garbage-collected until the anonymous instance is removed. This is a common contributor to memory leaks in GUI applications. Using a static nested class, or a lambda that does not capture the outer instance, avoids this issue.

Common Pitfalls and Compatibility Notes

Anonymous classes have been part of Java since version 1.1, so they work with all modern Java versions. An anonymous class is serializable only when the interface it implements or the class it extends is serializable. Even then, serialized forms are fragile because the generated class name includes a synthetic counter, so they can break when code changes.

Another pitfall is shadowing. If an anonymous class declares a field with the same name as a field in the enclosing scope, the anonymous class field shadows the outer field. Similar collisions can happen with local variables and method parameters. Use distinct names to avoid confusion.

Finally, anonymous classes cannot be abstract, and they cannot have static initializers or static members (except constant variables). If you need static state, a named nested class is the correct choice. These constraints are part of the language specification and have not changed across Java versions.

Java Anonymous Class: Syntax, Scope, and Code Examples | RYUSLOG DEV