Что такое анонимный класс java
Перейти к содержимому

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

  • автор:

#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 ("Летающий мотоцикл"); >>;

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

Если вы много программировали на языке 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 , и не имеет собственного имени.

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

Pro Java

Анонимный класс – это локальный класс без имени. Можно объявить анонимный (безымянный) класс, который может расширить (extends) другой класс или реализовать (implements) интерфейс. Объявление такого класса выполняется одновременно с созданием его объекта посредством оператора new.

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

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

Так как у анонимного класса нет имени, то, как я уже говорил, в теле класса нельзя определить его конструктор. Это одно из основных ограничений анонимных классов. Любые аргументы, которые вы укажете в круглых скобках, стоящих за именем родительского класса в определении анонимного класса, неявно передаются конструктору родительского класса . Чаще всего анонимные классы применяются для расширения родительских классов простыми классами, которые не требуют аргументов конструктора, поэтому скобки в определении анонимного класса зачастую пусты.

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

Теперь рассмотрим на практике простой пример:

AN0001

На примере слева красными рамками обозначены фрагменты кода где объявляются анонимные классы. Первый имплементирует интерфейс Iout, второй расширяет класс External. Обратите внимание на двоеточие после фигурных скобок закрывающих объявление класса , оно является обязательным, так как по существу объявление анонимного класса представляет собой выражение . Это означает, что его можно записать как часть большего выражения, например вызова метода . Я привел достаточно простые примеры, хотя синтаксис все равно может показаться сложным. И начал я с объявления анонимного класса реализующего интерфейс Iout, так как на этом примере более легко понять почему анонимные классы называются анонимными – так как у них нет имени. Как вы помните невозможно создать экземпляр интерфейса, поскольку он является полностью абстрактным классом, а тут мы создаем класс реализующий интерфейс, но у этого класса нет имени и ссылку на этот анонимный класс мы присваиваем интерфейсной переменной iout. Надеюсь все просто? Ни кто не запутался?

Во втором же примере не все так прозрачно, так как может показаться что у класса все же есть имя External, но это не так. Класс по прежнему анонимный, так как он расширяет класс External и не является экземпляром класса External, хотя ссылка на этот анонимный класс присвоена переменной класса External, что является нормальным и обычным. Надеюсь что опять ни кто не запутался 🙂

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

Первую строку выводит метод outPrint(), другие – extPrint().

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

AN0003

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

Вывод у программы следующий:

AN0004

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

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

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

Анонимные классы, так же как и локальные, имеют доступ к локальным переменным своего блока кода которые объявлены как final или они должны быть effectively final .

Анонимные классы имеют доступ ко всем членам своего вешнего класса.

Если в анонимном классе объявлена переменная с таким же именем как и в окружающем классе, то она затеняет переменную окружающего класса.

Анонимный класс не может определять статические поля, методы или классы, кроме констант static final. Интерфейс не может быть объявлен анонимно, потому что нет способа реализовать интерфейс без имени. Так же как и локальные классы, анонимные классы не могут быть public, private, protected или static.

В анонимном классе вы не можете объявить статические инициализационные блоки.

В анонимном классе вы не можете объявить интерфейс.

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

В анонимном классе вы можете объявить:

  • Поля
  • Дополнительные методы (даже если этих методов нет в классе родителе)
  • Инициализационные блоки экземпляра
  • Локальные классы

Ну и теперь рассмотрим примеры всех вышеприведенных утверждений.

AN0006

Начнем пожалуй с этого 🙂 то есть с .this. В Принципе это можно было еще на первом примере показать, но чет запамятовал. В общем первый пример мутировал в текущий. Я добавил строку str в класс Outer и затем обратился к ней из анонимного класса. Эти две строки подсвечены желтым.

Теперь у программы такой вывод:

AN0007

В общем тут нет ни чего не обычного, так как анонимные классы это те же внутренние классы, но с некоторыми ограничениями которые мы уже озвучили чуть выше.

А теперь попробуем смоделировать конструктор анонимного класса 🙂

AN0008

Чтобы смоделировать конструктор в анонимном классе нам будет необходим класс от которого будет наследоваться наш анонимный класс. С анонимным классом имплементирующем интерфейс такой номер не прокатит, так как у интерфейсов нет конструкторов.

Нам так же необходимо чтобы у родительского класса был конструктор. И тогда, как уже говорилось, все аргументы которые будут указаны в круглых скобках при создании анонимного класса будут передаваться конструктору родительского класса, но так же будут доступны и в анонимном. Причем даже если в родительском классе эти аргументы используются только в конструкторе и ни где более, то есть не сохраняются в полях родительского класса, эти аргументы все равно будут доступны в анонимном классе. Но они должны быть переданы в конструктор с модификатором final или быть effectively final .

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

Вывод программы представлен ниже:

AN0009

Как видно из вывода программы аргумент i переданный в конструктор суперкласса Base доступен как в конструкторе суперкласса, так и в инициализаторе и методах анонимного класса, который в данном случае является и наследником класса Base и так же внутренним его классом. Именно поэтому ему доступно private static поле i. Я умышленно дал одинаковые названия аргументу и полю, чтобы продемонстрировать области видимости переменных и полей.

И еще один пример на тему эмулирования конструкторов в анонимных классах:

AN0010

AN0011

AN0012

В это программе не используются статические методы и поля для того чтобы продемонстрировать доступ к полям экземпляров в классах Base и External. По существу у нас есть два анонимных класса. Анонимный класс в классе Base является и вложенным в него и его же наследником, а в классе External анонимный класс является наследником класса Base и внутренним для класса External. Именно по этому в первом случае анонимный класс имеет доступ к private полю str класса Base, а во втором не имеет и использует для доступа к нему унаследованный метод getStr(). Собственно из вывода программы видно как она работает 🙂

AN0013

Я немного изменил предыдущий пример, чтобы продемонстрировать наследование. Теперь класс External является наследником класса Base. И поэтому от туда был убран метод getThis() и добавлены конструкторы. Класс Main тоже претерпел небольшие изменения, туда была добавлена одна строка.

println ( «e.getStr #000000» size=»2″> + e . getStr ()) ;

Обратите внимание что анонимный класс находящийся внутри класса External так же является наследником класса Base.

AN0014

AN0015

С теорией по внутренним классам вроде пока все 🙂 В следующем посте немного попрактикуемся по всей теме внутренних классов. Может по ходу практики еще всплывет что-то, что возможно было упущено в теории.

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

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