Анонимные (anonimous) классы и обработчики событий

Анонимный (безымянный) класс объявляется без задания имени класса и переменных данного безымянного типа – задаётся только конструктор класса вместе с его реализацией. У анонимного класса может быть только один экземпляр, причём он создаётся сразу при объявлении класса. Поэтому перед объявлением анонимного класса следует ставить оператор new. Анонимный класс должен быть наследником какого-либо класса или интерфейса, и соответствующий тип должен быть указан перед списком параметров конструктора.

Синтаксис задания анонимного класса таков:

new ИмяПрародителя(список параметров конструктора) {

тело конструктора

}

 

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

 

Пример использования анонимного класса в “слушателе” события (о них речь пойдёт в следующем параграфе):

 

 

addMouseMotionListener(

new java.awt.event.MouseMotionAdapter(){

public void mouseDragged(java.awt.event.MouseEvent e){

System.out.println("Mouse dragged at: x="+

e.getX()+" y="+e.getY()

);

}

}

);

Анонимные (anonimous) классы и слушатели событий (listeners)

Событие в Java (будем называть его программным событием, или, сокращённо, просто событием) – это объект, возникающий при наступлении какого-либо события в реальном мире при взаимодействии с ним компьютера (будем называть его физическим событием). Например, физическим событием может быть нажатие на клавишу клавиатуры. При наступлении некоторых физических событий возникают программные события – создаются объекты, имеющие тип, зависящий от того, какое событие наступило. Обработчики событий – подпрограммы, которые выполняют некоторый код при наступлении программного события. Например, код, который будет выполнен при нажатии пользователем на кнопку jButton1 во время работы приложения.

В Java к каждому объекту, поддерживающему работу с неким событием, могут добавляться слушатели (listeners) событий этого типа – объекты-обработчики событий. Они являются экземплярами специальных классов Listeners, в которых заданы методы, реагирующие на соответствующие типы событий.

Классы и интерфейсы для работы с событиями заданы в пакетах java.awt, java.awt.event и javax.swing.event.

Важнейшие типы событий:

 

В пакете java.awt:

java.awt.AWTEvent – абстрактный класс, прародительский для всех классов событий.

В пакете java.awt.event:

ActionEvent – событие действия (как правило, нажатие).

AdjustmentEvent – изменение значения в линии прокрутки (для компонентов с линией прокрутки).

ComponentEvent – компонент переместился, изменил размер или видимость (visibility) -показался или был скрыт.

ContainerEvent – содержимое компонента-контейнера изменилось – какой-либо компонент был в него добавлен или из него убран.

FocusEvent – компонент получил или потерял фокус.

HierarchyEvent – изменение положения компонента в физической иерархии (иерархии агрегации). Например, удаление родительского компонента, смена компонентом родителя (перетаскивание с одного компонента на другой), и т.п.

InputEvent – произошло событие ввода. Базовый класс для классов событий ввода (KeyEvent, MouseEvent)

InputMethodEvent – произошло событие ввода. Содержит информацию об обрабатываемом тексте.

ItemEvent – событие, возникающее в случае, если пункт (item) был отмечен (selected) или с него была снята отметка (deselected).

KeyEvent – событие нажатия на клавишу.

MouseEvent – событие мыши.

PaintEvent – событие отрисовки. Служит для управления очередью событий и не может быть использовано для управления отрисовкой вместо методов paint или update.

TextEvent - событие, возникающее в случае, если текст в текстовом компоненте изменился.

WindowEvent – окно изменило статус (открылось, закрылось, максимизировалось, минимизировалось, получило фокус, потеряло фокус).

 

Также имеется большое количество событий в пакете javax.swing.event.

 

Для того, чтобы программа могла обработать событие какого-то типа, в приложение требуется добавить объект event listener (“слушатель события”) соответствующего типа. Этот тип - класс, который должен реализовать интерфейс слушателя, являющийся наследником интерфейса java.util.EventListener. Имя интерфейса слушателя обычно складывается из имени события и слова Listener.

Чтобы упростить реализацию интерфейсов, в Java для многих интерфейсов событий существуют так называемые адаптеры (adapters) – классы, в которых все необходимые методы интерфейсов слушателей уже реализованы в виде ничего не делающих заглушек. Так что в наследнике адаптера требуется только переопределение необходимых методов, не заботясь о реализации всех остальных. Перечислим важнейшие интерфейсы и адаптеры слушателей:

 

ActionEvent – ActionListener.

AdjustmentEvent – AdjustmentListener.

ComponentEvent – ComponentListener - ComponentAdapter.

ContainerEvent – ContainerListener - ContainerAdapter.

FocusEvent – FocusListener - FocusAdapter.

HierarchyEvent – HierarchyBoundsListener - HierarchyBoundsAdapter.

InputEvent – нет интерфейсов и адаптеров.

InputMethodEvent – InputMethodListener.

ItemEvent – ItemListener.

KeyEvent – KeyListener - KeyAdapter.

MouseEvent - MouseListener - MouseAdapter.

- MouseMotionListener - MouseMotionAdapter. По-английски motion – “движение”. Событие возникает при движении мыши.

- MouseWheelListener-MouseWheelAdapter. По-английски wheel – “колесо”. Событие возникает при прокручивании колёсика мыши.

PaintEvent – нет интерфейсов и адаптеров.

TextEvent – TextListener.

WindowEvent - WindowListener - WindowAdapter.

- WindowFocusListener. Событие возникает при получении или потере окном фокуса.

- WindowStateListener. Событие возникает при изменении состояния окна.

 

Все компоненты Swing являются потомками javax.swing.JComponent. А в этом классе заданы методы добавления к компоненту многих из упомянутых слушателей:

addComponentListener, addFocusListener и т.д. В классах компонентов, обладающих специфическими событиями, заданы методы добавления слушателей этих событий.

Повторим теперь код, приведённый в предыдущем параграфе, с разъяснениями:

 

addMouseMotionListener(

new java.awt.event.MouseMotionAdapter(){

public void mouseDragged(java.awt.event.MouseEvent e){

System.out.println("Mouse dragged at: x="+

e.getX()+" y="+e.getY()

);

}

}

);

 

В качестве параметра метода addMouseMotionListener выступает анонимный класс типа java.awt.event.MouseMotionAdapter, переопределяющий метод mouseDragged.

В интерфейсе MouseMotionListener имеется два метода:

 

mouseDragged(MouseEvent e);

mouseMoved(MouseEvent e)

 

Поскольку мы не переопределили заглушку “ mouseMoved ”, наш объект-обработчик событий типа MouseEvent (движение мыши порождает события именно такого типа) не будет ничего делать для обычного движения. А вот метод mouseMoved в нашем объекте-обработчике переопределён, поэтому при перетаскиваниях (когда идёт движение мыши с нажатой кнопкой) будет выводиться текст в консольное окно.

Допустим, мы поместили код с добавлением слушателя в обработчик события нажатия на какую-либо кнопку (например, jButton1). После срабатывания обработчика наш компонент (главная форма приложения, если мы добавили слушателя ей), станет обрабатывать события типа MouseMotion. При этом на каждое такое событие в консольное окно будет осуществляться вывод позиции мыши, но только в том случае, если идёт перетаскивание – когда в пределах формы мы нажали кнопку мыши и не отпуская её перетаскиваем. Причём неважно, какая это кнопка (их можно при необходимости программно различить).

Если мы расположим на форме панель jPanel1, и заменим вызов addMouseMotionListener (реально это вызов this.addMouseMotionListener) на jPanel1.addMouseMotionListener – слушатель расположится не в форме (объект this), а в панели. В этом случае события перетаскивания будут перехватываться только панелью. При этом важно, в какой области началось перетаскивание, но не важно, в какой области оно продолжается – события от перетаскивания, начавшегося внутри панели, будут возникать даже при перемещении курсора мыши за пределы окна приложения.

Если нажать на кнопку добавления слушателя два раза – в списке слушателей станет два объекта-обработчика событий MouseEvent. Объекты из списка слушателей обрабатывают события по очереди, в порядке их добавления. Поэтому каждое событие будет обрабатываться дважды. Если добавить ещё объекты слушателей - будет обрабатываться соответствующее число раз. Конечно, в реальных ситуациях если добавляют более одного объекта-слушателя для события, они не повторяют одни и те же действия, а по-разному реагируют на это событие. Либо они являются экземплярами одного класса, но с разными значениями полей данных, либо, что чаще – экземплярами разных классов. Например, для события MouseEvent существует интерфейc MouseListener и реализующий его адаптер MouseAdapter, имеющий четыре метода:

mouseClicked(MouseEvent e)

mouseEntered(MouseEvent e)

mouseExited(MouseEvent e)

mouseReleased(MouseEvent e)

Экземпляры классов-наследников MouseAdapter будут совсем по-другому обрабатывать события MouseEvent. Аналогично, можно создать экземпляр наследника MouseMotionListener, который будет реагировать не на перетаскивание, а на простое движение мыши.

Обычно классы слушателей, наследующие от адаптеров, делают анонимными, совмещая декларацию, реализацию и вызов экземпляра класса.

Краткие итоги по главе 11

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

  • Вложенные (nested) классы и интерфейсы – используются для задания совершенно самостоятельных классов и интерфейсов внутри классов. Должны при задании иметь модификатор static. Имя класса верхнего уровня используется в качестве квалификатора в пространстве имён, во всём остальном они ведут себя как обычные классы.
  • Внутренние (inner) классы – служат для создания экземпляров, принадлежащих экземплярам класса верхнего уровня. То есть их экземпляры не могут существовать вне объектов верхнего уровня. Не допускается создания методов класса или переменных внутреннего класса. Внутренний класс задаётся так же, как вложенный, но только без модификатора static перед именем этого класса. Использование внутренних классов позволяет реализовать в Java большинство возможностей модулей из процедурных языков программирования – в этом случае в качестве модуля выступает внешний класс.
  • Локальные (local) классы – задаются внутри блоков программного кода в методах или блоках инициализации. Они носят вспомогательный характер, и область видимости и жизни экземпляров этих классов ограничивается соответствующим блоком программного кода. Как и в случае внутренних классов, это позволяет реализовать в Java возможности модулей из процедурных языков программирования. И в этом случае в качестве модуля также выступает внешний класс.
  • Анонимные (anonimous) классы – совмещают декларацию, реализацию и вызов. Не имеют ни имени, ни конструктора. Их обычно используют в обработчиках событий.

 

ü Анонимный класс объявляется без задания имени класса и переменных данного безымянного типа – задаётся только конструктор класса вместе с его реализацией. У анонимного класса может быть только один экземпляр, причём он создаётся сразу при объявлении класса. Поэтому перед объявлением анонимного класса следует ставить оператор new. Безымянный класс должен быть наследником какого-либо класса или интерфейса, и соответствующий тип должен быть указан перед списком параметров конструктора.

ü Синтаксис задания безымянного класса таков:

new ИмяПрародителя(список параметров конструктора) {

тело конструктора

}

ü Программные события в Java – объекты, имеющие тип, зависящий от того, какое физическое событие наступило. Обработчики событий – подпрограммы, которые выполняют некоторый код при наступлении программного события.

ü В Java обработчики событий являются объектами – экземплярами специальных классов Listeners (“слушателей”). В этих классах заданы методы, реагирующие на соответствующий тип событий. Объекты обработчиков можно добавлять к компонентам, способным перехватывать соответствующие типы событий.

ü Классы и интерфейсы для работы с событиями заданы в пакетах java.awt, java.awt.event и javax.swing.event.

ü Классы, отвечающие за прослушивание событий, реализуют соответствующие интерфейсы - наследники интерфейса java.util.EventListener. Для того, чтобы упростить реализацию интерфейсов, в Java для многих интерфейсов событий существуют так называемые адаптеры (adapters) – классы, в которых все необходимые методы интерфейсов слушателей уже реализованы в виде ничего не делающих заглушек. Так что в наследнике адаптера требуется только переопределение необходимых методов, не заботясь о реализации всех остальных. Обычно эти классы делают анонимными, совмещая декларацию, реализацию и вызов.

Задания