Чтение онлайн

на главную - закладки

Жанры

Философия Java3

Эккель Брюс

Шрифт:

Замыкания и обратные вызовы

Замыканием (closure) называется вызываемый объект, который сохраняет информацию о контексте, он был создан. Из этого определения видно, что внутренний класс является объектно-ориентированным замыканием, поскольку он не только содержит информацию об объекте внешнего класса («место создания»), но к тому же располагает ссылкой на весь объект внешнего класса, с помощью которой он может манипулировать всеми членами этого объекта, в том числе и закрытыми (private).

При обсуждении того, стоит ли включать в Java некое подобие указателей,

самым веским аргументом «за» была возможность обратных вызовов (callback). В механизме обратного вызова некоторому стороннему объекту передается информация, позволяющая ему затем обратиться с вызовом к объекту, который произвел изначальный вызов. Это очень мощная концепция программирования, к которой мы еще вернемся. С другой стороны, при реализации обратного вызова на основе указателей вся ответственность за его правильное использование возлагается на программиста. Как было показано ранее, язык Java ориентирован на безопасное программирование, поэтому указатели в него включены не были.

Замыкание, предоставляемое внутренним классом, — хорошее решение, гораздо более гибкое и безопасное, чем указатель. Рассмотрим пример:

//: innerclasses/CalIbacks.java

// Использование внутренних классов

// для реализации обратных вызовов

package innerclasses;

import static net.mindview.util.Print.*;

interface Incrementable { void incrementO,

}

// Простая реализация интерфейса: class Call eel implements Incrementable { private int i = 0. public void incrementO { i++;

print(i);

class Mylncrement {

public void increment О { System, out. pri ntlnC'flpy гая операция") }; public static void f(MyIncrement mi) { mi.incrementО; }

}

// Если класс должен вызывать метод increment О // по-другому, необходимо использовать внутренний класс: class Callee2 extends Mylncrement { private int i = 0, private void increment О { super.increment: i++;

print(i):

}

private class Closure implements Incrementable { public void increment О {

// Указывается метод внешнего класса;

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

Са11ее2.this.increment;

}

}

Incrementable getCallbackReferenceO { return new ClosureO;

class Caller {

private Incrementable callbackReference;

Caller(Incrementable cbh) { callbackReference = cbh, }

void go { callbackReference incrementO; }

}

public class Callbacks {

public static void main(String[] args) { Call eel cl = new CalleelO; Callee2 c2 = new Callee2; Mylncrement.f(c2), Caller callerl = new Caller(cl); Caller caller2 = new Caller(c2.getCallbackReferenceO); callerl. goO; callerl.goO; caller2.go; caller2.go;

}

} /* Output: Другая операция 1 1 2

Другая операция 2

Другая операция 3

*///:-

Этот пример также демонстрирует различия между реализацией интерфейса внешним или внутренним классом. Класс Calleel — наиболее очевидное решение задачи с точки зрения программирования. Класс Callee2 наследует

от класса Mylncrement, в котором уже есть метод increment, выполняющий действие, никак не связанное с тем, что ожидает от него интерфейс Incrementable. Когда класс Mylncrement наследуется в Callee2, метод increment нельзя переопределить для использования в качестве метода интерфейса Incrementable, поэтому нам приходится предоставлять отдельную реализацию во внутреннем классе. Также отметьте, что создание внутреннего класса не затрагивает и не изменяет существующий интерфейс внешнего класса.

Все элементы, за исключением метода getCallbackReference, в классе Callee2 являются закрытыми. Для любой связи с окружающим миром необходим интерфейс Incrementable. Здесь мы видим, как интерфейсы позволяют полностью отделить интерфейс от реализации.

Внутренний класс Closure просто реализует интерфейс Incrementable, предоставляя при этом связь с объектом Callee2 — но связь эта безопасна. Кто бы ни получил ссылку на Incrementable, он в состоянии вызвать только метод incrementO, и других возможностей у него нет (в отличие от указателя, с которым программист может вытворять все, что угодно).

Класс Caller получает ссылку на Incrementable в своем конструкторе (хотя передача ссылки для обратного вызова может происходить в любое время), а после этого использует ссылку для «обратного вызова» объекта Callee.

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

Внутренние классы и система управления

В качестве более реального пример использования внутренних классов мы рассмотрим то, что я буду называть здесь системой управления (control framework).

Каркас приложения (application framework) — это класс или набор классов, разработанных для решения определенного круга задач. При работе с каркасами приложений обычно используется наследование от одного или нескольких классов, с переопределением некоторых методов. Код переопределенных методов адаптирует типовое решение, предоставляемое каркасом приложения, к вашим конкретным потребностям. Система управления представляет собой определенный тип каркаса приложения, основным движущим механизмом которого является обработка событий. Такие системы называются системами, управляемыми по событиям (event-driven system). Одной из самых типичных задач в прикладном программировании является создание графического интерфейса пользователя (GUI), всецело и полностью ориентированного на обработку событий.

Чтобы на наглядном примере увидеть, как с применением внутренних классов достигается простота создания и использования библиотек, мы рассмотрим систему, ориентированную на обработку событий по их «готовности». Хотя в практическом смысле под «готовностью» может пониматься все, что угодно, в нашем случае она будет определяться по показаниям счетчика времени. Далее приводится общее описание управляющей системы, никак не зависящей от того, чем именно она управляет. Нужная информация предоставляется посредством наследования, при реализации метода action.

Поделиться:
Популярные книги

Наследник чародея. Школяр. Книга первая

Рюмин Сергей
1. Наследник чародея
Фантастика:
городское фэнтези
5.00
рейтинг книги
Наследник чародея. Школяр. Книга первая

Проданная невеста

Wolf Lita
Любовные романы:
любовно-фантастические романы
5.80
рейтинг книги
Проданная невеста

Гардемарин Ее Величества. Инкарнация

Уленгов Юрий
1. Гардемарин ее величества
Фантастика:
городское фэнтези
попаданцы
альтернативная история
аниме
фантастика: прочее
5.00
рейтинг книги
Гардемарин Ее Величества. Инкарнация

Черт из табакерки

Донцова Дарья
1. Виола Тараканова. В мире преступных страстей
Детективы:
иронические детективы
8.37
рейтинг книги
Черт из табакерки

Новый Рал 9

Северный Лис
9. Рал!
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Новый Рал 9

Росток

Ланцов Михаил Алексеевич
2. Хозяин дубравы
Фантастика:
попаданцы
альтернативная история
фэнтези
7.00
рейтинг книги
Росток

Пять попыток вспомнить правду

Муратова Ульяна
2. Проклятые луной
Фантастика:
фэнтези
эпическая фантастика
5.00
рейтинг книги
Пять попыток вспомнить правду

Александр Агренев. Трилогия

Кулаков Алексей Иванович
Александр Агренев
Фантастика:
альтернативная история
9.17
рейтинг книги
Александр Агренев. Трилогия

Наследник с Меткой Охотника

Тарс Элиан
1. Десять Принцев Российской Империи
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Наследник с Меткой Охотника

Хозяйка старой пасеки

Шнейдер Наталья
Фантастика:
попаданцы
фэнтези
7.50
рейтинг книги
Хозяйка старой пасеки

Выйду замуж за спасателя

Рам Янка
1. Спасатели
Любовные романы:
современные любовные романы
7.00
рейтинг книги
Выйду замуж за спасателя

Искатель 2

Шиленко Сергей
2. Валинор
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Искатель 2

Мастер Разума III

Кронос Александр
3. Мастер Разума
Фантастика:
героическая фантастика
попаданцы
аниме
5.25
рейтинг книги
Мастер Разума III

Родословная. Том 2

Ткачев Андрей Юрьевич
2. Линия крови
Фантастика:
городское фэнтези
аниме
фэнтези
5.00
рейтинг книги
Родословная. Том 2