Back to Blog
Java

Java Anonymous Object: Syntax and Use Cases

java anonymous object: Learn how to create anonymous objects in Java, when to use them, and how they compare to lambdas and named classes.

anonymous classesJava syntaxlambda expressionsinner classesevent listeners
Diagram showing an anonymous object in Java created from an interface, with a note about its one-time use.

What Is a Java Anonymous Object?

In Java, an anonymous object is an instance of an anonymous class—a class defined and instantiated in a single expression, without a name. The syntax looks like this:

Runnable task = new Runnable() { @Override public void run() { System.out.println("Running task"); } };

Here, new Runnable() { ... } creates an object of an unnamed class that implements Runnable. The object is assigned to a variable of the interface type. Anonymous objects are useful when you need a one-off implementation of an interface or an extension of a class, and you don't want to create a separate named class.

Creating an Anonymous Object from an Interface or Abstract Class

You can create an anonymous object from any interface or abstract class, as long as you implement all required methods. For example, a Comparator for custom sorting:

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

The anonymous class implements Comparator<String> and provides the compare method. The object is passed directly to sort. This pattern is common when the comparison logic is only needed in one place.

You can also extend a concrete class and override methods:

Thread thread = new Thread() { @Override public void run() { System.out.println("Custom thread"); } };

Here, the anonymous object is an instance of a subclass of Thread. The class body defines the override.

Common Use Cases for Anonymous Objects

Anonymous objects appear frequently in event handling, callbacks, and functional-style operations. In Swing or Android, you often see:

button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Clicked"); } });

Before lambdas were introduced in Java 8, anonymous classes were the standard way to pass behavior. They are still used when the interface has multiple abstract methods (so it is not a functional interface) or when you need to override more than one method.

Another common case is initializing collections with custom behavior, such as a HashMap with an overridden put method for logging:

Map<String, Integer> map = new HashMap<>() { @Override public Integer put(String key, Integer value) { System.out.println("Put " + key); return super.put(key, value); } };

This is a double-brace initialization, which creates an anonymous subclass of HashMap. It works, but it has subtle implications for memory and equality, as discussed later.

Anonymous Objects vs. Lambda Expressions

For functional interfaces—interfaces with a single abstract method—lambdas provide a more concise syntax. The Runnable example from earlier becomes:

Runnable task = () -> System.out.println("Running task");

Lambdas are not anonymous classes; they are implemented differently and have different capture rules. The following table summarizes the key differences:

AspectAnonymous ClassLambda Expression
SyntaxVerbose, requires full method definitionConcise, no method name
Functional interfaceCan implement any interface or classOnly for functional interfaces
this referenceRefers to the anonymous objectRefers to the enclosing instance
Variable captureCan capture effectively final variablesCan capture effectively final variables
Generated bytecodeNew class file per anonymous classInvokedynamic with lambda metafactory

The this difference is important. Inside an anonymous class, this refers to the anonymous object. Inside a lambda, this refers to the enclosing object. This affects how you access instance fields and methods.

Variable Capture and Effectively Final Rules

Both anonymous classes and lambdas can only capture local variables that are effectively final—meaning they are not reassigned after initialization. This rule exists because the captured value is copied into the anonymous class instance at creation time. If the variable were mutable, the copy would become stale.

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

If you try to modify base after the anonymous object is created, the compiler rejects it. The same restriction applies to lambdas. However, anonymous classes can also access instance fields and this directly, which lambdas cannot do in the same way.

Memory and Performance Considerations

Each anonymous class definition compiles to a separate .class file, such as OuterClass$1.class. This increases the number of class files in your application, which can affect startup time and memory footprint if used excessively. Additionally, an anonymous object holds an implicit reference to its enclosing instance, which can prevent garbage collection if the anonymous object outlives the enclosing object. This is a common source of memory leaks in long-lived applications.

For example, if you register an anonymous ActionListener on a static component, the listener keeps a reference to the enclosing activity or frame. If the enclosing object is no longer needed, it cannot be collected because the listener still references it. Using a static nested class or a lambda that does not capture the enclosing instance avoids this problem.

Performance-wise, creating an anonymous object is similar to creating any other object. The overhead is in class loading and allocation, not in method calls. For high-frequency operations, reusing a single instance or using a lambda (which may be cached by the JVM) can reduce allocation pressure.

Limitations and Maintainability Tradeoffs

Anonymous objects are best for small, one-off implementations. They become hard to read when the class body grows beyond a few methods. They also cannot have explicit constructors, so you cannot pass arguments to initialize state—you must rely on instance initializers or captured variables. If you need multiple instances with different configurations, a named class with a constructor is clearer.

Another limitation is that anonymous classes cannot be static, so they always have an enclosing instance reference. This is rarely a problem for short-lived objects but can be a concern in static contexts or when you want to avoid the implicit reference.

From a maintainability perspective, named classes are easier to test and reuse. If the same behavior is needed in multiple places, extract it into a named class. Anonymous objects are appropriate when the logic is trivial and tightly coupled to the surrounding code.

java anonymous object: Practical Usage and Code Examples | RYUSLOG DEV