Перейти к основному содержимому

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

Иногда бывает необходимо создать только один объект некоторого класса. Причём его имя даже не важно. Такой класс можно определить как анонимный. Часто анонимные классы создаются там, где требуется объект некоторого интерфейс, чтобы не определять отдельный класс.

Общий синтаксис определения анонимного класса следующий:

new SuperType(аргументы для конструктора суперкласса) {
// поля и методы анонимного класса
}

Здесь SuperType может быть интерфейсом, тогда анонимный класс реализует этот интерфейс. SuperType также может быть классом, тогда анонимный класс расширяет этот класс.

Анонимный внутренний класс не может иметь конструкторов, поскольку имя конструктора должно совпадать с именем класса, а у класса нет имени. Вместо этого аргументы конструирования передаются конструктору супер-класса (если в супер-классе определен конструктор с параметрами).

Если SuperType представляет интерфейс, то аргументы для конструктора естественно не передаются (ведь у интерфейсов нет конструктора), и просто указываются пустые скобки:

new InterfaceType() {
// поля и методы анонимного класса
}

Анонимный класс вместо интерфейса​

Распространенным вариантом использования анонимных классов является их создание там, где требуется объект интерфейса. Например:

public class Program {
public static void main(String[] args) {
var rectangle = new Movable() {
public void move(int distance) {
System.out.printf("Перемещаем прямоугольник на %d пикселей\n", distance);
}
};
moveObject(rectangle, 56); // Перемещаем прямоугольник на 56 пикселей
}

// перемещаем объект Movable на расстояние distance
static void moveObject(Movable obj, int distance) {
obj.move(distance);
}
}

interface Movable {
void move(int distance);
}

Основные моменты:

  • У нас есть интерфейс Movable, в котором определен метод move().
interface Movable {
void move(int distance);
}
  • Определён метод moveObject(), в который передаётся объект интерфейса Movable. И далее у этого объекта вызывается метод move().
static void moveObject(Movable obj, int distance) {
obj.move(distance);
}
  • Определена переменная rectangle, которая по сути представляет собой объект анонимного класса, который, в свою очередь, реализует интерфейс Movable.
var rectangle = new Movable() {
public void move(int distance) {
System.out.printf("Перемещаем прямоугольник на %d пикселей\n", distance);
}
};

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

  • И в конце вызывается метод moveObject() с передачей в него объекта анонимного класса.
moveObject(rectangle, 54);

При этом нам необязательно создавать отдельно переменную, которая содержит ссылку на объект анонимного класса. Мы можем создать объект анонимного класса непосредственно при вызове метода:

public class Program {
public static void main(String[] args) {
// передаем объект анонимного класса
moveObject(new Movable() {
public void move(int distance) {
System.out.printf("Человек идет на расстояние %d метров\n", distance);
}
},
100);
}

static void moveObject(Movable obj, int distance) {
obj.move(distance);
}
}

interface Movable {
void move(int distance);
}

Анонимный класс вместо класса​

Менее распространенный вариант, но, тем не менее, возможный - это расширение анонимным классом обычного класса. Например:

public class Program {
public static void main(String[] args) {
printPerson(
new Person("Tom") {
private String company = "Yandex";

// реализация метода print
@Override
void print() {
System.out.printf("Employee. Name: %s; Company: %s\n", super.getName(), company);
}
}
);
}

static void printPerson(Person person) {
person.print();
}
}

abstract class Person {
private String name;

Person(String name) {
this.name = name;
}

String getName() {
return name;
}

abstract void print();
}

Рассмотрим основные моменты:

  • Здесь определён абстрактный класс Person, который представляет некоторого человека. Через конструктор он принимает имя и определяет абстрактный метод print().
  • Статический метод printPerson() принимает объект Person и вызывает у него метод print().
static void printPerson(Person person) {
person.print();
}
  • При вызове метода printPerson() передаётся объект анонимного класса. Причём, как мы знаем, в Java мы не можем использовать вызов конструктора для создания объекта абстрактного класса. И в данном случае вызов new Person("Tom") означает создание объекта анонимного класса, который расширяет класс Person и передает в его конструктор строку "Tom".

Затем в фигурных скобках определяется тело анонимного класса:

{
private String company = "Yandex";

@Override
void print() {
System.out.printf("Employee. Name: %s; Company: %s\n", super.getName(), company);
}
}

В реализации добавляется дополнительное поле (для хранения компании) и определяется реализацию для абстрактного метода print(). Причем в реализации анонимного класса мы можем, как и в производных классах, ссылаться на функционал базового класса через ключевое слово super.

Инициализаторы в анонимных классах​

Как выше говорилось, мы не можем определить конструктор в анонимных классах, однако можем определить блок инициализатора для начальной инициализации:

printPerson(
new Person("Tom") {
private String company;

// блок инициализатора
{
company = "Sber";
}

// реализация метода print
@Override
void print() {
System.out.printf("Employee. Name: %s; Company: %s\n", super.getName(), company);
}
}
);