Анонимные классы
Если вы много программировали на языке Java, вы могли заметить, что можно объявить классы, вложенные в другие классы. В этой статье рассматривается один из особых типов вложенности, называемый «анонимным классом». Для начала взглянем на следующий простой пример:
static class B <
> // статический вложенный класс
class C <
> // внутренний класс
В этом примере есть несколько типов вложенных и внутренних классов. Вложенный класс, не объявленный статическим, называется внутренним классом. В данном коде B является вложенным классом, а C — вложенным и внутренним классом.
Главной целью данной статьи являются анонимные классы. Вы можете понять суть анонимных классов, изучив приведенный выше пример. Основная особенность — анонимный класс не имеет имени. Анонимный класс является подклассом существующего класса (в примере Base) или реализации интерфейса.
Поскольку анонимный класс не имеет имени, он не может иметь явный конструктор. Также к анонимному классу невозможно обратиться извне объявляющего его выражения, за исключением неявного обращения посредством объектной ссылки на суперкласс или интерфейс. Анонимные классы никогда не могут быть статическими, либо абстрактными, и всегда являются конечными классами. Кроме того, каждое объявление анонимного класса уникально. Например, в следующем фрагменте кода объявляются два различных анонимных класса:
Каждый анонимный класс объявляется внутри выражения.
Типичный пример применения
Рассмотрим типичную ситуацию, в которой вы могли бы применить анонимный класс:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class AnonDemo2 <
public static void main ( String args []) <
// создать JFrame и добавить к нему перехватчик
// событий для обработки закрытия окна
// создать JPanel и добавить к фрейму
JPanel panel = new JPanel () ;
panel.setPreferredSize ( new Dimension ( 300 , 300 )) ;
frame.getContentPane () .add ( panel ) ;
Этот пример отображает 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 )) ;
// отсортировать список обычным способом (по возрастанию)
// отсортировать список по убыванию,
// используя функцию, реализованную объектом
// при помощи анонимного класса
Программа выполняет первую сортировку очевидным способом. Для того чтобы выполнить сортировку по убыванию программа должна определить объект функции Comparator.Этот объект реализует логику сравнения для сортировки объектов Integer в убывающем порядке.
В этой программе используется анонимный класс, реализующий интерфейс java.util.Comparator. Если такой тип сортировки производится только в одном месте приложения, то имеет смысл использовать анонимный класс, но если такая сортировка нужна во многих местах, то больший смысл имеет определить класс на более высоком уровне вложенности, или статический вложенный класс,
и реализовать логику сортировки только один раз.
Программа отображает следующую информацию:
Примеры использования
Давайте рассмотрим последний пример, в котором демонстрируется несколько приемов использования анонимных классов:
// установить значение afield
// получить значение afield
public class AnonDemo4 <
static A createAnon () <
final int dlocal = 40 ;
// возвратить из метода f() экземпляр
// анонимного класса, порожденного из A
public static void main ( String args []) <
A anonref = createAnon () ;
В этом примере метод createAnon объявляет анонимный класс и возвращает ссылку типа суперкласс (A) на экземпляр анонимного класса. Это означает, что экземпляр анонимного класса может быть использован вне объявляющего его контекста (createAnon). Далее вызывается метод getValue объектной ссылки на анонимный класс.
Вспомните, что анонимные классы не имеют имени, следовательно, они не могут иметь явные конструкторы. Но существует несколько способов преодоления такого ограничения. Когда создается экземпляр анонимного класса, например:
автоматически вызывается конструктор суперкласса.
Инициализация экземпляра анонимного класса происходит обычным путем, то есть
работают как обычно. Эти механизмы могут использоваться для выполнения части работы, которая в нормальных случаях выполняется в конструкторе.
Пример AnonDemo4 имеет одну необычную особенность. Переменная dlocal объявляется как финальная. Если убрать ключевое слово final из объявления — компилятор выдаст ошибку. Почему? Потому что есть возможность, и это демонстрирует данная программа, обратиться к анонимному объекту вне контекста, в котором он был объявлен. Если сделать такое обращение, какое значение будет иметь переменная dlocal, учитывая, что это локальная переменная, объявленная в методе createAnon? Это классический вопрос программирования, возникающий при обращении к неверному фрейму стека.
Для решения этой проблемы локальная переменная должна быть финальной, то есть связанной с определенным значением, которое может быть использовано вместо самой переменной (dlocal). То есть, вместо использования «dlocal» используется значение «40».
Анонимные классы очень полезны, но нельзя их переоценивать. Какой-либо другой тип класса может быть более хорошим выбором при использовании в приложении более чем одного анонимного класса с одинаковой логикой, либо в тех случаях, когда логика такого класса является сложной, либо при наличии глубокой вложенности классов. Кроме того, объявления анонимных классов трудно читаемы, поэтому вы должны делать их простыми.
Ссылки
Дополнительная информация об анонимных классах находится в разделе 5.4 «Анонимные внутренние классы» учебника «Язык программирования Java(tm)» Арнольда, Гослинга и Холмса (Arnold, Gosling, Holmes).
Также обратитесь к разделу «Преимущество статических классов-членов над нестатическими» в книге «Эффективное руководство по языку программирования Java» Джошуа Блоха (Joshua Bloch).
Внутренние и вложенные классы java. Часть 1

Цель статьи: Рассказать о внутренних, вложенных, локальных, анонимных классах. Показать примеры их использования. Написать и протестировать классы в коде на java. Рассказать о свойствах этих классов. Материал предназначен для лучшего понимания безымянных классов, лямбда выражений, адаптеров и многопоточности. То есть перед их изучением.
Небольшое вступление. Предлагаю вашему вниманию цикл из трех статей.
В них я рассказываю о внутренних, вложенных, локальных, анонимных классах. Речь идет о терминологии и применении. Для этих статей я написал довольно много кода.
Это учебный код, а не руководство к действию. То есть сам код я написал для лучшего понимания. Также я постарался объяснить работу учебного кода. На написание данной публикации, ушло довольно много времени. Публикация состоит из трех частей. Прошу отнестись с пониманием.
Для лучшего изучения материала у вас должна быть некоторая подготовка.
То есть вам нужно знать: синтаксис языка java, область видимости переменных, классы, статические и нестатические члены класса, создание экземпляров класса, наследование, модификаторы доступа.
Начнем с того, что же такое внутренние и вложенные классы. Посмотрим терминологию, встречающуюся в документации и литературе:
В Java существуют 4 типа вложенных (nested) классов:
- Статические вложенные классы
- Внутренние классы
- Локальные классы
- Анонимные (безымянные) классы
Существуют четыре категории вложенных классов:
- статический класс-член (static member class),
- не статический класс-член (nonstatic member class),
- анонимный класс (anonymous class)
- и локальный класс (local class).
Попытаемся разобраться, что же это такое.
Начнем немного отдаленно, так как всё это имеет непосредственное отношение к нашим вопросам. Вспомним объектно-ориентированное программирование. Отношения композиции и наследования.
В своей книге «Java 2 Руководство разработчика» Майкл Морган очень хорошо и подробно описывает взаимосвязи классов и объектов. Мы рассмотрим некоторые из них. Взаимосвязь «это — есть — то» выражается наследованием, а взаимосвязь «имеет часть» описывается композицией.
В наших примерах мы в основном рассматриваем композицию. Так как вложенные классы — это и есть часть чего-то. То есть у нас есть класс оболочка и вложенный класс определенный внутри класса оболочки. Пример композиции: машина имеет двигатель, двери, 4 колеса, корпус. И мы можем описать машину с помощью внутренних (Inner) классов.
Пример такого использования вы можете найти в книге Брюса Эккеля «Философия Java»
Есть некоторое предупреждение автора по использованию кода в таком виде:
Выше я не случайно упомянул наследование и композицию. Это напрямую относится к дальнейшему материалу.
Статические вложенные классы
Определение вложенных классов:
То есть класс просто определен внутри другого, даже не важно статически определен или не статически. Вложенный класс создается для того, чтобы обслуживать окружающий его класс. Если вложенный класс оказывается полезен в каком-либо ином контексте, он должен стать классом верхнего уровня.
Вложенные классы применяются в тех случаях, когда нужно написать небольшой вспомогательный код для другого класса. Вложенный класс создают также, чтобы скрыть его переменные и методы от внешнего мира. Таким образом, вложенный класс еще один элегантный способ ограничения области видимости. Внутренние классы также есть смысл использовать, если предполагается, что они будут использовать элементы родителя, чтобы не передавать лишнего в конструкторах.
Пример вложенного класса вы можете увидеть в документации Оракле:
У нас нет, пока что, никакого контекста использования данной конструкции. С таким же успехом вложенный класс мы можем назвать вместо: «Вложенный класс» (NestedClass) — «Внутренний класс» InnerClass. Далее будем разбираться, в чем же отличия, и в каких контекстах используются классы. Брюс Эккель пишет в книге «Философия Java» так:
Документацию Oracle вы можете посмотреть по этой ссылке: >>>
Терминология:
Существует четыре категории вложенных классов:
- Статические вложенные классы и не статические вложенные классы. Вложенные классы, объявленные статически, называются вложенными статическими классами.
- Внутренние классы — когда объект внутреннего класса связан с объектом обрамляющего класса. Не статические вложенные классы называются внутренними классами, если они связанны с внешним классом.
- Локальные классы — объявленные внутри блока кода и не являющиеся членом обрамляющего класса. В этом случае можно рассматривать класс как локальную переменную типа класс.
- Анонимные классы – наследуемые, от какого либо класса, классы в которых при объявлении не задано имя класса.
Причины использования вложенных классов такие. Если класс полезен только для одного другого класса, то вполне логично встроить его в этот класс и хранить их вместе. Использование вложенных классов увеличивает инкапсуляцию. Рассмотрим два класса верхнего уровня, A и B, где B нужен доступ к членам, которые иначе были бы объявлены закрытыми.
Скрывая класс «B» в пределах класса «А», члены класса «А» могут быть объявлены закрытыми, и «B» может получить доступ к ним. Кроме того, сам «B» может быть скрыт от внешнего мира.
Продемонстрируем это в коде:
Использование вложенных классов приводит к более читабельному и поддерживаемому коду: Размещение класса ближе к тому месту, где он будет использован, делает код более читабельным.
Статические Вложенные Классы
Static Nested Classes
Причины использования статических вложенных классов такие.
Для случая, когда связь между объектом вложенного класса и объектом внешнего класса не нужна, можно сделать вложенный класс статическим(static).
Так как внутренний класс связан с экземпляром, он не может определить в себе любые статические члены.
Статические вложенные классы не имеют ограничений по объявлению своих данных и полей как static.
Из вложенного статического класса мы не имеем доступа к внешней не статической переменной внешнего класса.
Приведенный ниже код демонстрирует это:
Вывод: Мы не имеем доступа к не статическому полю внешнего класса, через статический контекст вложенного класса. Это подобно тому, как мы не имеем доступа из статического метода к нестатическим переменным класса. Точно также из статического вложенного класса мы не имеем доступа к нестатическим переменным внешнего класса.
Но мы имеем доступ к приватным статическим полям внешнего класса из вложенного статичного класса.
Приведенный ниже фрагмент кода демонстрирует это:
Все вопросы, комментарии, дополнения, критика приветствуются.
Продолжение следует…
Часть 2 >>>
Если у вас есть возможность, вам пригодилось, и вы можете помочь, то нажмите кнопку поддержать автора материально.
Java Anonymous Class
In this tutorial, you will learn about anonymous classes in Java with the help of examples.
In Java, a class can contain another class known as nested class. It’s possible to create a nested class without giving any name.
A nested class that doesn’t have any name is known as an anonymous class.
An anonymous class must be defined inside another class. Hence, it is also known as an anonymous inner class. Its syntax is:
Anonymous classes usually extend subclasses or implement interfaces.
Here, Type can be
- a superclass that an anonymous class extends
- an interface that an anonymous class implements
The above code creates an object, object1 , of an anonymous class at runtime.
Note: Anonymous classes are defined inside an expression. So, the semicolon is used at the end of anonymous classes to indicate the end of the expression.
Example 1: Anonymous Class Extending a Class
Output
In the above example, we have created a class Polygon . It has a single method display() .
We then created an anonymous class that extends the class Polygon and overrides the display() method.
When we run the program, an object p1 of the anonymous class is created. The object then calls the display() method of the anonymous class.
Example 2: Anonymous Class Implementing an Interface
Output
In the above example, we have created an anonymous class that implements the Polygon interface.
Advantages of Anonymous Classes
In anonymous classes, objects are created whenever they are required. That is, objects are created to perform some specific tasks. For example,
Here, an object of the anonymous class is created dynamically when we need to override the display() method.
Модуль 8. Урок 3. Анонимные классы в Java. — Введение в Java
Анонимные классы объявляются без указания в коде имени класса.
Анонимные классы могут быть созданы:
1) как реализация интерфейса
На практике, чаще, реализуют функциональные интерфейсы, с помощью анонимных классов, не смотря на наличие лямбд. Вот перечень функциональных интерфейсов из пакета java.util.function . Это не магия, а просто набор готовых заготовок для ваших будущих реализаций.
2) как наследник определённого класса
Анонимный класс может быть как статическим, так и нестатическим. Это напрямую зависит от того, статическим или нестатическим является блок, в котором анонимный класс был объявлен.
Если анонимный класс объявлен в статическом блоке:
То его второй экземпляр можно создать так: Consumer otherInstance = foo.getClass().newInstance(); (эту строку нужно завернуть в try-catch или пробросить исключения выше). И переиспользуем ту же логику, но уже в другом инстансе: otherInstance.accept("Работа второго экземпляра анонимного класса в статическом методе."); .
Если же анонимный класс был объявлен внутри нестатического блока, то для создания второго экземпляра анонимного класса нужно передать в его конструктор ссылку на обрамляющий класс. В противном случае получим InstantiationException .
Вот как это выглядит в коде:
Любой анонимный внутренний класс может за один раз реализовать только один интерфейс. Так же, за один раз можно либо расширить класс, либо реализовать интерфейс, но не одновременно.
Анонимные классы полезны в некоторых "узких" участках кода, когда нет необходимости их потом переиспользовать где-то еще. Чаще всего на практике используют лишь один экземпляр анонимного класса. Если же реализацию интерфейса придется использовать много раз в коде — то лучше использовать лямбда-выражения (лямбды), которые стали доступны начиная с JAVA 8 .
Например, реализацию интерфейса Consumer , в текущем уроке, можно переписать с помощью лямбд:
Преимущества и недостатки между анонимными классами и лямбдами постепенно меняются в сторону лямбд. Об этих различиях можно почитать в статье на Хабре.
Отдельно стоит упомянуть, что не все версии Android поддерживают лямбды. И многим будет проще использовать анонимные классы, чем прикручивать лямбды.