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

Что такое анонимный класс в java

  • автор:

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

Если вы много программировали на языке Java, вы могли заметить, что можно объявить классы, вложенные в другие классы. В этой статье рассматривается один из особых типов вложенности, называемый «анонимным классом». Для начала взглянем на следующий простой пример:

static class B > // статический вложенный класс

class C > // внутренний класс

void f () class D > // локальный внутренний класс
>

void g () // анонимный класс
Base bref = new Base () void method1 () >
> ;
>
>

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

Главной целью данной статьи являются анонимные классы. Вы можете понять суть анонимных классов, изучив приведенный выше пример. Основная особенность — анонимный класс не имеет имени. Анонимный класс является подклассом существующего класса (в примере Base) или реализации интерфейса.

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

Base bref1 = new Base () <
void method1 () <>
> ;

Base bref2 = new Base () void method1 () <>
> ;

Каждый анонимный класс объявляется внутри выражения.

Типичный пример применения

Рассмотрим типичную ситуацию, в которой вы могли бы применить анонимный класс:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class AnonDemo2 public static void main ( String args [])

// создать JFrame и добавить к нему перехватчик
// событий для обработки закрытия окна

JFrame frame = new JFrame ( «AnonDemo2» ) ;
frame.addWindowListener ( new WindowAdapter () public void windowClosing ( WindowEvent e ) System.exit ( 0 ) ;
>
>) ;

// создать JPanel и добавить к фрейму

JPanel panel = new JPanel () ;
panel.setPreferredSize ( new Dimension ( 300 , 300 )) ;
frame.getContentPane () .add ( panel ) ;

frame.pack () ;
frame.setVisible ( true ) ;
>
>

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

WindowsListener является интерфейсом. Класс реализации должен определить все методы, указанные в интерфейсе. WindowAdapter реализует интерфейс, используя фиктивные методы, например:

public abstract class WindowAdapter implements WindowListener,
WindowStateListener, WindowFocusListener public void windowOpened ( WindowEvent e ) >

public void windowClosing ( WindowEvent e ) >

public void windowClosed ( WindowEvent e ) >

public void windowIconified ( WindowEvent e ) >

public void windowDeiconified ( WindowEvent e ) >

public void windowActivated ( WindowEvent e ) >

public void windowDeactivated ( WindowEvent e ) >

public void windowStateChanged ( WindowEvent e ) >

public void windowGainedFocus ( WindowEvent e ) >

public void windowLostFocus ( WindowEvent e ) >
>

Демонстрационное приложение AnonDemo2 должно переопределить только один из этих методов, а именно — windowClosing. То есть, приложение наследует класс адаптера и переопределяет один метод. Подкласс используется в приложении только один раз и имеет очень простую логику. Вот почему анонимный класс является хорошим выбором в данном случае. Анонимный класс расширяет класс WindowAdapter, переопределяя один метод. WindowAdapter, в свою очередь, реализует класс WindowListener, используя фиктивные, ничего не выполняющие методы.

Сортировка списка с использованием анонимных классов

Рассмотрим другой пример. Предположим, что вы имеете список List объектов Integer, и хотите отсортировать список и в возрастающем (по умолчанию) и в убывающем порядке. Вот программа, выполняющая эту задачу:

public class AnonDemo3 public static void main ( String args [])

// создать ArrayList и добавить в него
// несколько объектов Integer

List list = new ArrayList () ;
list.add ( new Integer ( 37 )) ;
list.add ( new Integer ( — 59 )) ;
list.add ( new Integer ( 83 )) ;

// отсортировать список обычным способом (по возрастанию)

Collections.sort ( list ) ;
System.out.println ( list ) ;

// отсортировать список по убыванию,
// используя функцию, реализованную объектом
// при помощи анонимного класса

Collections.sort ( list, new Comparator () public int compare ( Object o1, Object o2 ) int a = (( Integer ) o1 ) .intValue () ;
int b = (( Integer ) o2 ) .intValue () ;
return a < b ? 1 : a == b ? 0 : - 1 ;
>
>) ;
System.out.println ( list ) ;
>
>

Программа выполняет первую сортировку очевидным способом. Для того чтобы выполнить сортировку по убыванию программа должна определить объект функции Comparator.Этот объект реализует логику сравнения для сортировки объектов Integer в убывающем порядке.

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

class MyComparator implements Comparator <
.
>

и реализовать логику сортировки только один раз.

Программа отображает следующую информацию:

[ — 59 , 37 , 83 ]
[ 83 , 37 , — 59 ]

Примеры использования

Давайте рассмотрим последний пример, в котором демонстрируется несколько приемов использования анонимных классов:

class A <
int afield;

// установить значение afield

A ( int afield ) this .afield = afield;
>

// получить значение afield

int getValue () return afield;
>
>

public class AnonDemo4 static A createAnon () final int dlocal = 40 ;

// возвратить из метода f() экземпляр
// анонимного класса, порожденного из A

// вызвать конструктор суперкласса
return new A ( 10 ) int bfield = 20 ;
int cfield;

int getValue () return afield + bfield + cfield + dlocal;
>
> ;
>

public static void main ( String args []) A anonref = createAnon () ;

В этом примере метод createAnon объявляет анонимный класс и возвращает ссылку типа суперкласс (A) на экземпляр анонимного класса. Это означает, что экземпляр анонимного класса может быть использован вне объявляющего его контекста (createAnon). Далее вызывается метод getValue объектной ссылки на анонимный класс.

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

автоматически вызывается конструктор суперкласса.

Инициализация экземпляра анонимного класса происходит обычным путем, то есть

int bfield = 20 ;
<
cfield = 30 ;
>

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

Пример AnonDemo4 имеет одну необычную особенность. Переменная dlocal объявляется как финальная. Если убрать ключевое слово final из объявления — компилятор выдаст ошибку. Почему? Потому что есть возможность, и это демонстрирует данная программа, обратиться к анонимному объекту вне контекста, в котором он был объявлен. Если сделать такое обращение, какое значение будет иметь переменная dlocal, учитывая, что это локальная переменная, объявленная в методе createAnon? Это классический вопрос программирования, возникающий при обращении к неверному фрейму стека.

Для решения этой проблемы локальная переменная должна быть финальной, то есть связанной с определенным значением, которое может быть использовано вместо самой переменной (dlocal). То есть, вместо использования «dlocal» используется значение «40».

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

Ссылки

Дополнительная информация об анонимных классах находится в разделе 5.4 «Анонимные внутренние классы» учебника «Язык программирования Java(tm)» Арнольда, Гослинга и Холмса (Arnold, Gosling, Holmes).

Также обратитесь к разделу «Преимущество статических классов-членов над нестатическими» в книге «Эффективное руководство по языку программирования Java» Джошуа Блоха (Joshua Bloch).

A может Вас также заинтересует что-нибудь из этого:
  1. Разное → Теория и практика Java: Динамическая компиляция и измерение производительности
  2. Java Standard Edition → Блокировки
  3. Java Standard Edition → Производительность операций ввода/вывода в Java
  4. Java сниппеты → Методы для работы с переменным количеством аргументов
  5. Разное → C# глазами Java программиста
  6. Java сниппеты → Использование readResolve

Как создать анонимный класс java

В Java анонимный класс — это класс без имени, который определяется непосредственно внутри другого класса или метода. Он обычно используется для реализации интерфейсов или классов-родителей с небольшим количеством методов, и не требует создания отдельного класса для реализации этих методов.

Вот пример создания анонимного класса:

public class Main  public static void main(String[] args)  Runnable runnable = new Runnable()  @Override public void run()  System.out.println("Этот код выполняется в анонимном классе"); > >; Thread thread = new Thread(runnable); thread.start(); > > 

В этом примере мы

  • создаем объект типа Runnable с помощью анонимного класса, который реализует метод run()
  • затем мы создаем объект Thread , передавая ему объект runnable в качестве параметра конструктора, и запускаем поток. Обратите внимание, что анонимный класс определяется непосредственно внутри вызова конструктора Thread , и не имеет собственного имени.

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

Анонимные классы в 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()    ...   >   >; 

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

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

 ListRunnable> actions = new ArrayListRunnable>();  actions.add(new Runnable()    @Override   public void run()    ...   >   >); 

Мы должны использовать этот синтаксис с большой осторожностью, так как он может легко ухудшить читабельность кода, особенно когда реализация метода 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);   >   >; 

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

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

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

Чтобы компилятор решил, что переменная на самом деле неизменяемая, в коде должно быть только одно место, в котором мы присваиваем ей значение. Мы могли бы найти больше информации об эффективно окончательных переменных в нашей статье « Почему локальные переменные, используемые в лямбда-выражениях, должны быть окончательными или фактически окончательными? ”

Отметим только, что, как и любой внутренний класс, анонимный класс может получить доступ ко всем членам окружающего его класса .

4. Варианты использования анонимного класса​

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • 1. Введение
  • 2. Объявление анонимного класса
    • 2.1. Расширить класс
    • 2.2. Реализовать интерфейс
    • 3.1. Конструктор
    • 3.2. Статические члены
    • 3.3. Объем переменных
    • 4.1. Иерархия классов и инкапсуляция
    • 4.2. Более чистая структура проекта
    • 4.3. Слушатели событий пользовательского интерфейса

    #17 – Вложенные и анонимные классы

    #17 – Вложенные и анонимные классы

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

    Видеоурок

    Вложенный класс

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

    К примеру, мы разрабатываем класс «Автомобиль». В классе можно прописать множество методов и полей, но описать поведение двигателя или отдельных частей машины бывает куда проще за счёт еще одного дополнительного класса.

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

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

    Пример вложенного класса:

    class Auto < String model; class Engine < private boolean working = false; public void work () < working = true; System.out.print("Двигатель запущен!"); >> Engine en = new Engine(); > public class Main < public static void main(String[] args) < Auto bmw = new Auto(); bmw.en.work(); >>

    Подобный принцип используется во многих классах Java. К пример, если вспомнить класс «System», то в нём также есть вложенный класс и доступ к нему мы получаем за счёт объекта «out»:

    // System - основной класс // out - объект на основе вложенного класса // print - метод, что находиться внутри вложенного класса System.out.print("");
    Анонимные классы

    Анонимные классы — это классы, что не имеют имени и их создание происходит в момент инициализации объекта.

    Такие классы много где используются в Java и с ними вы ещё часто будете сталкиваться.

    Рассмотрим небольшой пример. Предположим что у нас есть интерфейс, который необходимо реализовать. Для реализации можно создать отдельный класс, в нём реализовать интерфейс, далее создать объект на основе класса и через объект ссылаться на методы классы.

    Звучит логично, но не оправдано с точки зрения количества действий. Куда проще создать объект и для него сразу выполнить создание анонимного класса.

    Пример создания анонимного класса:

    Auto car = new Auto() < void flyingMoto () < System.out.print ("Летающий мотоцикл"); >>;

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *