Java Anonymous Classes

In the last lesson you learned about Java static nested classes. Those still had names. Now we look at a class with no name at all. It is called an anonymous class, and you write it and create an object from it in one expression.

πŸ€” The problem anonymous classes solve

Sometimes you need an object that does one small job, only once.

  • A method asks for a Runnable, an object with a run method.
  • A sort method asks for a Comparator, an object that compares two items.
  • You do not want a whole separate file and named class for something you use once.
  • The old way fills your project with tiny classes that exist only to be passed to one method.

Here is that heavy old way. We create a named class only to hand it to a thread.

// A whole named class, used only once
class PrintTask implements Runnable {
@Override
public void run() {
System.out.println("Task is running");
}
}
public class Main {
public static void main(String[] args) {
Runnable task = new PrintTask();
new Thread(task).start();
}
}

That works, but PrintTask exists only to be used on one line. An anonymous class lets you skip the separate class and write the body right where you need the object.

🧩 What is an anonymous class?

An anonymous class is a class with no name that you declare and create at the same time, in one expression. You use it to implement an interface or extend a class on the spot.

  • The shape looks like a normal new, but with a body in curly braces after it.
  • new SomeType() { ... } makes a new little class based on SomeType and fills in these methods.
  • Because it has no name, you can never make a second object from it. It exists for that one object.
new SomeType() {
// the methods you want to fill in go here
};

Here is the thread example from above, rewritten with an anonymous class. No separate PrintTask class anymore.

public class Main {
public static void main(String[] args) {
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Task is running");
}
};
new Thread(task).start();
}
}

The new Runnable() { ... } part creates an object whose class has no name. Inside the braces we write the one method Runnable needs, run.

Output

Task is running

🧩 The new Interface syntax, step by step

Let’s read each piece, because the braces in the middle of a new confuse almost everyone at first.

  • new Runnable() looks like creating a Runnable, but it is an interface, so you cannot create it directly. The braces make this legal.
  • The { ... } block is the body of a brand new unnamed class that implements Runnable.
  • Inside, public void run() fills in the one method the interface requires.
  • The final semicolon ends the whole statement, because this is one expression assigned to task, just like any assignment.

🧩 A Comparator as an anonymous class

A very common place for anonymous classes is sorting. Many sort methods ask for a Comparator, an object with a compare method that takes two items and says which comes first. A named class for that is overkill. This sorts a list of names by length using an anonymous Comparator.

import java.util.ArrayList;
import java.util.Comparator;
public class Main {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
names.add("Arjun");
names.add("Alex");
names.add("Riya");
names.sort(new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
System.out.println(names);
}
}

We pass the anonymous class straight into sort, with no variable in between. compare returns a negative number when a should come first, so a.length() - b.length() puts shorter names first. The rule lives right at the call site.

Output

[Alex, Riya, Arjun]

🧩 Extending a class, not just an interface

Anonymous classes are not only for interfaces. You can also extend a class and override one or two methods. The object you get is a small variation of the parent, made on the spot.

public class Main {
public static void main(String[] args) {
Thread worker = new Thread() {
@Override
public void run() {
System.out.println("Custom thread running");
}
};
worker.start();
}
}

Here new Thread() { ... } makes an object of an unnamed class that extends Thread, overriding run. So an anonymous class can implement an interface or extend a class. Either way, one object with custom code and no separate named class.

Output

Custom thread running

πŸ”’ Anonymous classes can capture local variables

An anonymous class can use variables from the method around it. This is called capture.

  • The anonymous class β€œcaptures” the local variable and remembers its value.
  • The captured variable must be effectively final: set once and never changed.
  • You do not have to write final, but the value must stay the same.
  • Java needs this because the object may run later, even on another thread, so the value must stay stable.

This builds a greeting prefix once, then an anonymous Runnable captures it.

public class Main {
public static void main(String[] args) {
String greeting = "Hello"; // set once, never changed
Runnable greeter = new Runnable() {
@Override
public void run() {
System.out.println(greeting + ", Alex");
}
};
greeter.run();
}
}

The run method uses greeting from main. Because greeting is never reassigned, Java captures it. Change it later and the code stops compiling, which the mistakes section covers.

Output

Hello, Alex

πŸͺ„ A lambda is shorter for functional interfaces

The Runnable and Comparator examples felt wordy because both interfaces have exactly one method. An interface with one method is a functional interface, and for those Java gives a much shorter tool: the lambda. Here is the same Runnable two ways.

public class Main {
public static void main(String[] args) {
// Anonymous class: the full, wordy way
Runnable oldWay = new Runnable() {
@Override
public void run() {
System.out.println("Old way running");
}
};
// Lambda: the short way for a functional interface
Runnable newWay = () -> System.out.println("New way running");
oldWay.run();
newWay.run();
}
}

Both are Runnable objects that do the same thing. The lambda threw away new Runnable(), @Override, and public void run(), because the compiler knew all of that from the single method. When an interface has one method, prefer a lambda. Read more in the lambda expressions lesson. Keep anonymous classes for cases a lambda cannot handle.

Output

Old way running
New way running

When a lambda cannot replace an anonymous class

A lambda only works for a functional interface, one with a single method. An anonymous class can do more. It can implement an interface with several methods, extend a class, hold its own fields, and override more than one method. For those cases the anonymous class is still your tool.

⚠️ Common Mistakes

The braces and the capture rule cause most of the trouble. Watch for these.

Using an anonymous class where a lambda is cleaner. For a one-method interface, the lambda says the same thing with far less noise.

// ❌ wordy anonymous class for a single-method interface
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Hi");
}
};
// βœ… a lambda says the same thing
Runnable taskClean = () -> System.out.println("Hi");

Changing a captured variable. A local variable used inside an anonymous class must be effectively final. Reassign it and the code will not compile.

// ❌ this will not compile: count is changed after it is captured
int count = 0;
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(count);
}
};
count = 5; // error: variable used in anonymous class must be effectively final
// βœ… leave the captured value alone, or use a fresh variable
int total = 0;
Runnable rOk = new Runnable() {
@Override
public void run() {
System.out.println(total);
}
};

Forgetting the closing semicolon. The whole new Type() { ... } is one expression, so it needs a semicolon after the closing brace when it ends a statement.

// ❌ missing the semicolon after the closing brace
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Run");
}
}
// βœ… the statement ends with a semicolon
Runnable rOk = new Runnable() {
@Override
public void run() {
System.out.println("Run");
}
};

Letting the body grow huge. An anonymous class is meant for a small, focused job. If it grows into many methods and lots of logic, give it a name and move it to its own class.

βœ… Best Practices

  • Prefer a lambda for one-method interfaces. Use an anonymous class only when a lambda cannot do the job: several methods, extending a class, or holding fields.
  • Keep the body small. If it grows large, it should become a named class.
  • Use them for one-off objects. If you need the same behavior in many places, a named class is clearer.
  • Do not fight the final rule. If you want to change a captured variable, the logic probably belongs in a regular loop or method.
  • Remember the semicolon. The whole expression ends with ; when it sits in a statement.

🧩 What You’ve Learned

Nicely done. Let’s recap anonymous classes.

  • βœ… An anonymous class is a class with no name that you declare and create in one expression.
  • βœ… You use it to implement an interface or extend a class right where you need the object.
  • βœ… The syntax is new Type() { ... }, with the method bodies inside the braces and a ; at the end.
  • βœ… It can capture local variables, but those variables must be effectively final.
  • βœ… For a functional interface (one method), a lambda is shorter, so prefer it there.
  • βœ… Use an anonymous class when a lambda cannot help: several methods, extending a class, or its own fields.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What is an anonymous class?

    Why: An anonymous class has no name and is declared and instantiated together in a single expression.

  2. 2

    What is the syntax for an anonymous class?

    Why: You write new Type() followed by a body in braces, ending with a semicolon.

  3. 3

    What must a local variable be to be captured inside an anonymous class?

    Why: A captured local variable must be effectively final so its value stays stable when the object runs.

  4. 4

    When should you prefer a lambda over an anonymous class?

    Why: For a single-method functional interface, a lambda is much shorter and clearer than an anonymous class.

πŸš€ What’s Next?

You now know the nested-class family, from inner classes to anonymous ones. Next we move to a topic every real program needs: handling things that go wrong while the code runs.

Introduction to Exceptions in Java

Share & Connect