76

76


DotNew dn - new DotNew(); DotNew.Inner dni = dn.new InnerO;

}

} III -

При создании объекта внутреннего класса указывается не имя внешнего класса DotNew, как можно было бы ожидать, а имяобъектавнешнего класса. Это также решает проблему видимости имен для внутреннего класса, поэтому мы не используем (а вернее, не можем использовать) запись вида dn.new DotNew. Inner().

Невозможно создать объект внутреннего класса, не имея ссылки на внешний класс. Но если создатьвложенный класс(статический внутренний класс), ссыл­ка на объект внешнего класса не нужна.

Рассмотрим пример использования .new в примере Parcel:

// innerclasses/Parcel3 java

// Использование new для создания экземпляров внутренних классов

public class Parcel3 { class Contents {

private int i = 11,

public int valueO { return i; }

}

class Destination {

private String label;

DestinationCString whereTo) { label = whereTo; } String readLabelO { return label; }

}

public static void main(String[] args) { Parcel3 p = new Parcel3(); II Для создания экземпляра внутреннего класса // необходимо использовать экземпляр внешнего класса: Pa reel 3. Contents с = p. new ContentsO; Parcel3.Destination d = p new Destination"Танзания");

}

} III -

Внутренние классы и восходящее преобразование

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

Для начала определим интерфейсы для предыдущих примеров:

// i nnerclasses/Desti nati on.java public interface Destination {

String readLabel(); } Hi­ll-. innerclasses/Contents.java public interface Contents {

int valueO; } ///-

Теперь интерфейсы Contents и Destination доступны программисту-клиенту. (Помните, что в объявлении interface все члены класса автоматически являются открытыми (public).)

При получении из метода ссылки на базовый класс или интерфейс возмож­ны ситуации, в которых вам не удастся определить ее точный тип, как здесь:

//. innerclasses/TestParcel.java

class Parcel4 {

private class PContents implements Contents { private int i = 11; public int valueO { return i; }

}

protected class PDestination implements Destination { private String label; private PDestination(String whereTo) { label = whereTo;

}

public String readLabelО { return label; }

}

public Destination destination(String s) { return new PDestination(s);

}

public Contents contents О {

return new PContentsО;

public class TestParcel {

public static void main(String[] args) { Parcel4 p = new Parcel4(); Contents с = p.contents О; Destination d = p.destinationC'TacMaHMfl"); // Запрещено - нет доступа к private-классу: //! Parcel4.PContents pc = p.new PContentsО;

}

} ///-

В класс Parcel4 было добавлено кое-что новое: внутренний класс PContents является закрытым (private), поэтому он недоступен для всех, кроме внешнего класса Рагсе14. Класс PDestination объявлен как protected, следовательно, доступ к нему имеют только класс Parcel4, классы из одного пакета с Рагсе14 (так как спецификатор protected также дает доступ з пределах пакета) и наследники класса Рагсе14. Таким образом, программист-клиент обладает ограниченной ин­формацией и доступом к этим членам класса. Более того, нельзя даже выпол­нить нисходящее преобразование к закрытому (private) внутреннему классу (или protected, кроме наследников), поскольку его имя недоступно, как показано в классе Test. Таким образом, закрытый внутренний класс позволяет разработ­чику класса полностью запретить использование определенных типов и скрыть все детали реализации класса. Вдобавок, расширение интерфейса с точки зре­ния программиста-клиента не будет иметь смысла, поскольку он не сможет по­лучить доступ к дополнительным методам, не принадлежащим к открытой части класса. Наконец, у компилятора Java появится возможность оптимизи­ровать код.

Внутренние классы в методах и областях действия

Ранее мы рассмотрели ряд типичных применений внутренних классов. В ос­новном ваш код будет содержать «простые» внутренние классы, смысл которых понять нетрудно. Однако синтаксис внутренних классов скрывает множество других, не столь тривиальных способов их использования: внутренние классы можно создавать внутри метода или даже в пределах произвольного блока. На то есть две причины:

•        как было показано ранее, вы реализуете некоторый интерфейс, чтобы за­тем создавать и возвращать ссылку его типа;

•        вы создаете вспомогательный класс для решения сложной задачи, но при этом не хотите, чтобы этот класс был открыт для посторонних.

В следующих примерах рассмотренная недавно программа будет изменена, благодаря чему у нас появятся:

•        класс, определенный внутри метода;

•        класс, определенный внутри области действия (блока), которая находит­ся внутри метода;

•        безымянный класс, реализующий интерфейс;

•        безымянный класс, расширяющий класс, у которого отсутствует конст­руктор по умолчанию;

•        безымянный класс, выполняющий инициализацию полей;

•       безымянный класс, осуществляющий конструирование посредством инициализации экземпляра (безымянные внутренние классы не могут иметь конструкторы).

Первый пример демонстрирует создание целого класса в контексте метода (вместо создания в контексте другого класса). Такие внутренние классы назы­ваютсялокальными:

//• innerclasses/Parce!5.java

// Вложение класса в тело метода.

public class Parcel5 {

public Destination dest(String s) {

class PDestination implements Destination { private String label; private PDestination(String whereTo) { label = whereTo;

}

public String readLabelO { return label; }                              _
Л

продолжение &

}

return new PDestination(s).

Report Page