Java Anonymous Inner Class: Syntax, Use Cases, Limits
java anonymous inner class: Explore Java anonymous inner class syntax, variable capture, memory implications, and practical use cases, plus how they compare to lambdas.
java anonymous inner class requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
Syntax and Basic Usage
An anonymous inner class is a class that is declared and instantiated in a single expression, without a name. It is used when you need a one-off implementation of an interface or a subclass of a class.
Runnable task = new Runnable() { @Override public void run() { System.out.println("Running task"); } };
The expression new Runnable() { ... } defines a class that implements Runnable and immediately creates an instance. The class body can override methods and define additional members, though the type of the variable is the interface or superclass.
Because the class has no name, you cannot refer to it elsewhere in the code. The instance is only accessible through the variable or as a direct argument to a method call.
Capturing Variables and Effectively Final
Anonymous inner classes can capture local variables from the enclosing scope, but only if those variables are final or effectively final. This rule exists because the anonymous class instance may outlive the method that created it, and Java needs to copy the values into the class instance.
int count = 10; Runnable task = new Runnable() { @Override public void run() { System.out.println(count); } };
If count is reassigned after being captured, the code will not compile. This is a common source of errors when converting from an inner class to a lambda, because lambdas have the same restriction.
The same rule applies to instance variables of the enclosing class. They are always accessible, but they are not copied; the anonymous class holds a reference to the enclosing instance.
Anonymous Inner Classes vs. Lambdas
Lambdas, introduced in Java 8, provide a more concise syntax for functional interfaces. For a single abstract method, a lambda can replace an anonymous inner class.
Runnable task = () -> System.out.println("Running task");
However, the two are not always interchangeable. An anonymous inner class can implement an interface with multiple methods, or extend a class. A lambda can only target a functional interface. Also, an anonymous inner class has its own this reference, while a lambda's this refers to the enclosing instance. This difference matters when you need to refer to the object itself inside the method.
For example, if you need to call a method defined in the anonymous class itself, you must use the anonymous class form. With a lambda, this points to the surrounding object, so such a call would not compile.
Common Use Cases
Anonymous inner classes are commonly used for event listeners, comparators, and thread tasks. For example, a button listener in Swing:
button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Clicked"); } });
Before lambdas, this was the standard pattern. Even today, anonymous inner classes are useful when the interface has multiple methods, such as Comparator (which has a single abstract method but also default methods) or when you need to extend a class like Thread.
Another common use is passing a custom comparator to a sorting method:
Collections.sort(list, new Comparator<String>() { @Override public int compare(String a, String b) { return a.length() - b.length(); } });
This pattern still appears in codebases that target Java 7 or earlier, and in contexts where the additional methods of the interface are needed.
Memory and Performance Considerations
Each anonymous inner class definition produces a separate class file at compile time. At runtime, the JVM loads that class, which adds memory overhead compared to a lambda. Lambdas use invokedynamic and are more lightweight because they don't generate a new class file per usage.
Anonymous inner classes also hold an implicit reference to the enclosing instance, which can cause memory leaks if the instance outlives the enclosing object. This is especially relevant in Android or long-lived applications. Lambdas do not capture the enclosing instance unless they reference its members.
For example, if an anonymous Runnable is passed to a background thread and the enclosing activity is destroyed, the anonymous class still references the activity, preventing garbage collection. This is a common source of memory leaks in UI frameworks.
Limitations and When to Avoid
Anonymous inner classes cannot have explicit constructors, so you cannot pass constructor arguments. They also cannot be reused; each expression creates a new instance. If you need multiple instances with the same behavior, a named class or a lambda (for functional interfaces) is often better.
Another limitation is that anonymous inner classes cannot be abstract, and they cannot have static members (except constant variables). They also cannot be used to implement an interface that has more than one abstract method unless you provide implementations for all of them.
If the implementation is complex or needs to be tested independently, a named inner class or a separate top-level class is more maintainable. Anonymous inner classes are best for short, single-use implementations.
Anonymous Inner Classes in Modern Java
With Java 8 and later, lambdas cover most functional interface use cases. However, anonymous inner classes remain relevant for:
- Implementing an interface with multiple abstract methods.
- Extending a class with additional behavior.
- Using
thisto refer to the anonymous instance itself. - Working with legacy code that predates lambdas.
When you encounter an anonymous inner class in existing code, you can often replace it with a lambda if the target type is a functional interface. The refactoring is straightforward, but you must verify that the semantics of this and variable capture are compatible.
For instance, if the anonymous class overrides toString() or has its own fields, a lambda cannot express that. In such cases, keep the anonymous inner class or extract a named class.