25

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) {

Report Page