При обсуждении того, стоит ли включать в 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 являются закрытыми. Для
Внутренний класс Closure просто реализует интерфейс Incrementable, предоставляя при этом связь с объектом Callee2 — но связь эта безопасна. Кто бы ни получил ссылку на Incrementable, он в состоянии вызвать только метод incrementO, и других возможностей у него нет (в отличие от указателя, с которым программист может вытворять все, что угодно).
Класс Caller получает ссылку на Incrementable в своем конструкторе (хотя передача ссылки для обратного вызова может происходить в любое время), а после этого использует ссылку для «обратного вызова» объекта Callee.
Главным достоинством обратного вызова является его гибкость — вы можете динамически выбирать функции, выполняемые во время работы программы.
Внутренние классы и система управления
В качестве более реального пример использования внутренних классов мы рассмотрим то, что я буду называть здесь