Анонимные классы

Анонимный класс объявляется и создаётся одним выражением. Он расширяет ровно один класс либо реализует ровно один интерфейс, но собственного имени и явного конструктора у него нет.

interface RetryPolicy {
    boolean shouldRetry(int attempt, Exception error);
}
 
RetryPolicy policy = new RetryPolicy() {
    private int failures;
 
    @Override
    public boolean shouldRetry(int attempt, Exception error) {
        failures++;
        return attempt < 3 && error instanceof IOException;
    }
};

После new RetryPolicy() { ... } создаются новый класс и его экземпляр. В теле можно объявить поля, методы, instance-блоки и переопределения.

Доступ к окружению

Локальные переменные захватываются только если они final или effectively final. Поля внешнего объекта доступны через Outer.this.

class Screen {
    private final String name = "orders";
 
    Runnable listener() {
        String event = "refresh"; // effectively final
        return new Runnable() {
            @Override
            public void run() {
                System.out.println(Screen.this.name + ": " + event);
            }
        };
    }
}

В анонимном классе this — экземпляр анонимного класса. В лямбде this сохраняет значение из окружающего контекста.

Анонимный класс или лямбда

Лямбда подходит функциональному интерфейсу и выражает одну операцию. Анонимный класс нужен, когда есть несколько методов, собственные поля, дополнительный метод или важна отдельная идентичность объекта.

Ограничения:

  • нельзя написать явный конструктор;
  • тип неудобно повторно использовать и тестировать;
  • diamond <> с анонимным классом ограничивает объявления новых не-переопределяющих методов;
  • избыточная логика внутри делает место вызова нечитаемым.

На собеседовании

Можно ли реализовать два интерфейса анонимным классом? Не напрямую: выражение имеет один объявленный базовый тип.

Что можно захватить из метода? final или effectively final локальные переменные.

Чем this отличается от лямбды? В анонимном классе это новый объект, в лямбде — окружающий.

Создаётся ли отдельный класс? Да, JVM исполняет экземпляр сгенерированного анонимного класса.

Связано: Функциональные интерфейсы, Виды классов Java, this Java.