Рубрики
Без рубрики

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

Узнайте об анонимных классах Java.

Автор оригинала: Andrew Shcherbakov.

1. Введение

В этом учебнике мы рассмотрим анонимные классы на Java.

Мы опишем, как мы можем объявить и создать экземпляры из них. Мы также кратко обсудим их свойства и ограничения.

2. Анонимная декларация класса

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

Мы можем либо расширить существующий класс, либо реализовать интерфейс.

2.1. Продлить класс

Когда мы мгновенно переводим анонимный класс из несуществующего, мы используем следующий синтаксис:

В скобках мы указать параметры, которые требуются конструктору класса, которые мы продлеваем:

new Book("Design Patterns") {
    @Override
    public String description() {
        return "Famous GoF book.";
    }
}

Естественно, если конструктор родительского класса не принимает никаких аргументов, мы должны оставить скобки пустыми.

2.2. Реализация интерфейса

Мы можем мгновенно анонимный класс из интерфейса, а также:

Очевидно, что интерфейсы Java не имеют конструкторов, поэтому скобки всегда остаются пустыми. Только так мы должны сделать это для реализации методов интерфейса:

new Runnable() {
    @Override
    public void run() {
        ...
    }
}

После того, как мы мгновенно присвоили анонимный класс, мы можем назначить этот экземпляр переменной, чтобы иметь возможность ссылаться на него где-то позже.

Мы можем сделать это с помощью стандартного синтаксиса для выражений Java:

Runnable action = new Runnable() {
    @Override
    public void run() {
        ...
    }
};

Как мы уже упоминали, анонимное классное заявление является выражением, поэтому оно должно быть частью заявления . Это объясняет, почему мы поставили заоколон в конце заявления.

Очевидно, что мы можем избежать присвоения экземпляра переменной, если создадим inline этого экземпляра:

List actions = new ArrayList();
actions.add(new Runnable() {
    @Override
    public void run() {
        ...
    }
});

Мы должны использовать этот синтаксис с большой осторожностью, поскольку он может легко пострадать от читаемости кода, особенно при реализации бегите () метод занимает много места.

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

Есть определенные особенности в использовании анонимных классов по отношению к обычным классам высшего уровня. Здесь мы кратко коснумся наиболее практических вопросов. Для получения наиболее точной и обновленной информации, мы всегда можем посмотреть на Спецификация языка Java .

3.1. Конструктор

Синтаксис анонимных классов не позволяет заставить их реализовать несколько интерфейсов. Во время строительства может существовать ровно один экземпляр анонимного класса . Поэтому они никогда не могут быть абстрактными. Поскольку у них нет имени, мы не можем расширить их. По этой же причине анонимные классы не могут иметь явно объявленных конструкторов.

На самом деле, отсутствие конструктора не представляет для нас никаких проблем по следующим причинам:

  1. мы создаем анонимные экземпляры классов в тот же момент, когда объявляем их
  2. из анонимных экземпляров классов мы можем получить доступ к локальным переменным и приложить членов класса

3.2. Статические члены

Анонимные классы не могут иметь статических членов, за исключением тех, которые являются постоянными.

Например, это не будет компиляция:

new Runnable() {
    static final int x = 0;
    static int y = 0; // compilation error!

    @Override
    public void run() {...}
};

Вместо этого мы получим следующую ошибку:

The field y cannot be declared static in a non-static inner type, unless initialized with a constant expression

3.3. Сфера переменных

Анонимные классы фиксируют локальные переменные, которые находятся в области блока, в котором мы объявили класс:

int count = 1;
Runnable action = new Runnable() {
    @Override
    public void run() {
        System.out.println("Runnable with captured variables: " + count);
    }           
};

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

Обратите внимание, что для того, чтобы иметь возможность использовать локальные переменные, они должны быть фактически окончательными. Так как JDK 8, это не требуется больше, что мы объявляем переменные с ключевым словом окончательный . Тем не менее эти переменные должны быть окончательный . В противном случае мы получаем ошибку компиляции:

[ERROR] local variables referenced from an inner class must be final or effectively final

Для того, чтобы компилятор решил, что переменная, по сути, неизменяема, в коде должно быть только одно место, в котором мы присваиваем ему значение. Более подробную информацию об эффективных конечных переменных мы можем найти в нашей статье « Почему локальные переменные, используемые в Lambdas, должны быть окончательными или фактически окончательными?»

Давайте просто упомянуть, что, как и каждый внутренний класс, анонимный класс может получить доступ ко всем членам его прилагаемого класса .

4. Случаи использования анонимного класса

Там может быть большое разнообразие приложений анонимных классов. Давайте рассмотрим некоторые возможные случаи использования.

4.1. Иерархия классов и инкапсуляция

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

4.2. Более чистая структура проекта

Мы обычно используем анонимные классы, когда мы должны изменить на лету внедрение методов некоторых классов. В этом случае мы можем избежать добавления новых (.java ) файлов в проект для определения классов высшего уровня. Это особенно верно, если этот класс высшего уровня будет использоваться только один раз.

4.3. Слушатели событий пользовательского интерфейса

В приложениях с графическим интерфейсом наиболее распространенным случаем использования анонимных классов является создание различных слушателей событий. Например, в следующем фрагменте:

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        ...
    }
}

мы создаем экземпляр анонимного класса, который реализует интерфейс ActionListener. Его actionPerformed метод срабатывает при нажатии кнопки пользователем.

С Java 8, выражения lambda, кажется, более предпочтительным способом, хотя.

5. Общая картина

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

Глядя на диаграмму, мы видим, что анонимные классы вместе с местные и нестатические члены формы так называемого внутренние классы . Вместе с статические классов, они образуют вложенные классы.

6. Заключение

В этой статье мы рассмотрели различные аспекты анонимных классов Java. Мы описали также общую иерархию вложенных классов.

Как всегда, полный код доступен в течение в нашем репозитории GitHub .