SwadhyAI Notes

All Java notes

Java Anonymous Classes

›Short versionOpen if you only have a minute.
  • An anonymous class is a subclass or interface implementation declared in a new expression. It has one instance and no name you can reuse.
  • The body can hold fields and extra methods. It cannot declare a constructor. Use an instance initializer if something must run when the instance is built.
  • new Superclass(args) { ... } calls that superclass constructor. new Interface() { ... } implements the interface.
  • Locals used inside the body must be effectively final. The object keeps a copy of the value, not a live binding.
  • Created from an instance method, the anonymous class also holds the enclosing instance, and can keep that outer object alive.
  • A lambda fits a functional interface with no need for fields. An anonymous class fits extra state, more than one method, or a real superclass.

The sort needs a comparison that exists for this one call: order titles by length, and do not bother naming a class the rest of the program will never mention. An is that comparison written at the place it is used, as a of Comparator with no name of its own.

A class born inside the expression

java
List<String> titles = new ArrayList<>(List.of("Dune", "Emma", "Circe"));
titles.sort(new Comparator<String>() {
    @Override
    public int compare(String left, String right) {
        return Integer.compare(left.length(), right.length());
    }
});

The new expression creates one object. Its class implements Comparator and overrides compare. You cannot write that class name in another method. If a second call needs the same order, name a class, or store this instance and reuse the reference.

Comparator is a , so a says the same thing in less space: titles.sort((left, right) -> Integer.compare(left.length(), right.length())). The lambda has no field of its own. The anonymous class can have one, which is why it is still the right tool when the behavior accumulates something.

java
Comparator<String> counting = new Comparator<String>() {
    private int calls;

    @Override
    public int compare(String left, String right) {
        calls++;
        return Integer.compare(left.length(), right.length());
    }

    int calls() {
        return calls;
    }
};

There is no constructor to initialize calls. The field default of 0 is the start. An instance initializer, a block of braces directly in the class body, runs when you need more than a default. A subclass of a class that only has an Account(int) constructor writes the argument at new: new Account(80) { ... }.

What it is allowed to capture

A local variable used in the body is . The anonymous object stores the value it saw. The method must not assign that variable afterward.

java
int limit = 5;
Comparator<String> shortFirst = new Comparator<String>() {
    @Override
    public int compare(String left, String right) {
        return Integer.compare(Math.min(left.length(), limit), Math.min(right.length(), limit));
    }
};
// limit = 8;  // illegal once the class body has read limit

The is captured as well when this expression appears in an instance method. The comparator can call outer instance methods. It also keeps the outer object reachable. A static method's anonymous class does not have an outer instance. It can still close over local variables that stay final.

Anonymous classes remain useful for interfaces with two methods, for a quick subclass in a test, and for objects that need fields. For a single method and no state, the lambda is the form readers now expect. Both are still ordinary objects at the call: the list holds a comparator, and dynamic dispatch runs whichever compare that object provided.

Terminal
$ javac SortTitles.java
$ java SortTitles

The anonymous comparator reads a local int named limit, and the next line of the method does limit++. Why does that fail to compile?

limit has to be effectively final once the class body uses it. The anonymous instance captured the value, not a slot that updates when the method assigns. limit++ would change the local after that capture, so the compiler rejects the assignment. Copy limit into a field of the anonymous class if the comparison needs a number it can change itself.