25
Создание внутренних классов
Внутренние классы создаются в точности так, как и следовало ожидать, — определение класса размещается внутри окружающего класса:
//: innerclasses/Parcel 1.java // Создание внутренних классов.
public class Parcel 1 {
class Contents {
private int i = 11,
public int valueO { return i; }
}
class Destination {
private String label, DestinationCString whereTo) { label = whereTo;
}
String readLabeK) { return label; }
}
// Использование внутренних классов имеет много общего // с использованием любых других классов в пределах Parcel 1: public void shipCString dest) {
Contents с = new ContentsO; Destination d = new Destination(dest); System.out.pri ntln(d readLabel()),
}
public static void main(String[] args) { Parcel 1 p = new Parcel 10. p.ship("Тасмания").
}
} /* Output: Тасмания *///:-
Если вам понадобится создать объект внутреннего класса где-либо, кроме как в не-статическом методе внешнего класса, тип этого объекта должен задаваться в форматеИмяВнешнегоКласса.ИмяВнутреннегоКласса, что и делается в методе main().
Связь с внешним классом
Пока что внутренние классы выглядят как некоторая схема для сокрытия имен и организации кода — полезная, но не особенно впечатляющая. Однако есть еще один нюанс. Объект внутреннего класса связан свнешним объектом-создателеми может обращаться к его членам без каких-либо дополнительных описаний. Вдобавок для внутренних классов доступны все без исключения элементы внешнего класса
[1]
. Следующий пример иллюстрирует сказанное:
//: innerclasses/Sequence,java // Хранение последовательности объектов
interface Selector { boolean endO, Object currentO; void nextO;
}
public class Sequence {
private Object[] objects;
private int next = 0,
public Sequence(int size) { items = new Object[size], }
public void add(Object x) {
if(next < items length)
iterns[next++] = x,
}
private class SequenceSelector implements Selector { private int i = 0.
public boolean endО { return i == items.length; } public Object current О { return i terns [ i D: } public void nextО { if(i < items.length) i++; }
}
public Selector selectorO {
return new SequenceSelectorO;
}
public static void main(String[] args) {
Sequence sequence = new Sequence(lO); for(int i = 0, i < 10; i++)
sequence.add(Integer.toString(i)), Selector selector = sequence getSelectorO; whileC!selector endO) {
System.out.println(selector.currentО + " "); selector.nextO;
}
}
} /* Output- 0 1 2 3 4 5 6 7 8 9 *///:-
Класс Sequence — не более чем «оболочка» для массива с элементами Object, имеющего фиксированный размер. Для добавления новых объектов в конец последовательности (при наличии свободного места) используется метод add(). Для выборки каждого объекта в последовательности Sequence предусмотрен интерфейс с именем Selector. Он позволяет узнать, достигнут ли конец последовательности (метод end()), обратиться к текущему объекту (метод current()) и перейти к следующему объекту последовательности (метод next()). Так как Selector является интерфейсом, другие классы вправе реализовать его по-своему, а передача его в параметре методов повышает универсальность кода.
Здесь SequenceSelector является закрытым (private) классом, предоставляющим функциональность интерфейса Selector. В методе main() вы можете наблюдать за процессом создания последовательности с последующим заполнением ее объектами String. Затем вызывается метод getSelector() для получения интерфейса Selector, который используется для перемещения по последовательности и выбора ее элементов.
На первый взгляд создание SequenceSelector напоминает создание обычного внутреннего класса. Но присмотритесь к нему повнимательнее. Заметьте, что в каждом из методов end(), current() и next() присутствует ссылка на items, а это не одно из полей класса SequenceSelector, а закрытое (private) поле объемлющего класса. Внутренний класс может обращаться ко всем полям и методам внешнего класса-оболочки, как будто они описаны в нем самом. Это весьма удобно, и вы могли в этом убедиться, изучая рассмотренный пример.
Итак, внутренний класс автоматически получает доступ к членам объемлющего класса. Как же это происходит? Внутренний класс содержит скрытую ссылку на определенный объект окружающего класса, ответственный за его создание. При обращении к члену окружающего класса используется эта (скрытая) ссылка. К счастью, все технические детали обеспечиваются компилятором, но теперь вы знаете, что объект внутреннего класса можно создать только в сочетании с объектом внешнего класса (как будет показано позже, если внутренний класс не является статическим). Конструирование объекта внутреннего класса требует наличия ссылки на объект внешнего класса; если ссылка недоступна, компилятор выдаст сообщение об ошибке. Большую часть времени весь процесс происходит без всякого участия со стороны программиста.
Конструкции .this и .new
Если вам понадобится получить ссылку на объект внешнего класса, запишите имя внешнего класса, за которым следует точка, а затем ключевое слово this. Полученная ссылка автоматически относится к правильному типу, известному и проверяемому на стадии компиляции, поэтому дополнительные издержки на стадии выполнения не требуются. Следующий пример показывает, как использовать конструкцию .this:
//: innerclasses/DotThis.java
// Обращение к объекту внешнего класса.
public class DotThis {
void f() { System.out.pnntlnCDotThis.fО"); } public class Inner {
public DotThis outer() {
return DotThis this.
// A plain "this" would be Inner's "this"
}
}
public Inner inner О { return new InnerO; } public static void main(String[] args) { DotThis dt = new DotThisO; DotThis Inner dti = dt.innerO; dti.outer().f();
}
} /* Output:
DotThis.f()
*///:-
Иногда бывает нужно приказать другому объекту создать объект одного из его внутренних классов. Для этого перед .new указывается ссылка на другой объект внешнего класса:
//: innerclasses/DotNew.java
// Непосредственное создание внутреннего класса в синтаксисе .new
public class DotNew {
public class Inner {}
public static void main(String[] args) {