Выбрать главу

}

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

}

public static void main(String[] args) { Parcel5 p = new Parcel50; Destination d = p destinationC'TacMaHHH");

}

} ///.-

Теперь класс PDestination является частью метода destination(), а не частью класса Parcel5. Поэтому доступ к классу PDestination возможен только из метода destination(). Обратите внимание на восходящее преобразование, производимое в команде return, — из метода возвращается лишь ссылка на базовый класс Destination, и ничего больше. Конечно, тот факт, что имя класса PDestination на­ходится внутри метода destination(), не означает, что объект PDestination после выхода из этого метода станет недоступным.

Идентификатор PDestination может использоваться для внутренних классов каждого отдельного класса в одном подкаталоге, без порождения конфликта имен.

Следующий пример демонстрирует, как можно вложить внутренний класс в произвольную область действия:

//. innerclasses/Parcel6 java

// Вложение класса в область действия

public class Parcel6 {

private void internalTracking(boolean b) { if(b) {

class TrackingSlip {

private String id;

TrackingSlipCString s) { id = s;

}

String getSlipO { return id; }

}

TrackingSlip ts = new TrackingSlipC'ожидание");

String s = ts getSlipO;

}

// Здесь использовать класс нельзя!

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

//! TrackingSlip ts = new Tracki ngSlipCx").

}

public void trackO { internalTracking(true), }

public static void main(String[] args) { Parcel6 p = new Parcel60; p trackO;

}

} ///:-

Класс TrackingSlip вложен в область действия команды if. Это не значит, что класс создается в зависимости от условия — он компилируется вместе со всем остальным кодом. Однако при этом он недоступен вне контекста, в котором был определен. В остальном он выглядит точно так же, как и обычный класс.

Безымянные внутренние классы

Следующий пример выглядит немного странно:

// innerclasses/Parcel7 java

// Метод возвращает экземпляр безымянного внутреннего класса

public class Parcel7 {

public Contents contents О {

return new Contents О { // Вставить определение класса private int i = 11; public int valueO { return i; } }. // В данной ситуации точка с запятой необходима

}

public static void main(String[] args) { Parcel7 p = new Parcel7(); Contents с = p.contents О;

}

} ///-

Метод contents() совмещает создание возвращаемого значения с определени­ем класса, который это возвращаемое значение и представляет! Вдобавок, этот класс является безымянным — у него отсутствует имя. Ситуация запутывается еще тем, что поначалу мы будто бы приступаем к созданию объекта Contents, а потом, остановившись перед точкой с запятой, говорим: «Стоп, а сюда я под­кину определение класса».

Такая необычная форма записи значит буквально следующее: «Создать объ­ект безымянного класса, который унаследован от Contents». Ссылка, которая возвращается при этом из выражения new, автоматически повышается до базо­вого типа Contents. Синтаксис записи безымянного внутреннего класса являет­ся укороченной формой записи такой конструкции:

//: innerclasses/Parcel7b.java

II Расширенная версия Parcel7.java

public class Parcel7b {

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

}

public Contents contents О { return new MyContentsО; } public static void main(String[] args) { Parcel 7b p = new Parcel7b(); Contents с = p contentsO;

}

} ///-

В безымянном внутреннем классе базовый класс Contents создается с ис­пользованием конструктора по умолчанию. Следующая программа показывает, как следует поступать, если базовый класс требует вызова конструктора с аргу­ментами:

// innerclasses/Parcel8 java

// Вызов конструктора базового класса.

public class Parcel8 {                                                                                                 продолжение &