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) {
DotNew dn - new DotNew(); DotNew.Inner dni = dn.new InnerO;
}
При создании объекта внутреннего класса указывается не имя внешнего класса DotNew, как можно было бы ожидать, а имя
Невозможно создать объект внутреннего класса, не имея ссылки на внешний класс. Но если создать
Рассмотрим пример использования .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"Танзания");
}
Внутренние классы и восходящее преобразование
Мощь внутренних классов по-настоящему проявляется при выполнении восходящего преобразования к базовому классу, и в особенности к интерфейсу. (Получение ссылки на интерфейс по ссылке на реализующий его объект ничем принципиально не отличается от восходящего преобразования к базовому классу.) Причина в том, что внутренний класс — реализация интерфейса — может быть абсолютно невидимым и недоступным окружающему миру, а это очень удобно для сокрытия реализации. Все, что вы при этом получаете, — ссылку на базовый класс или интерфейс.
Для начала определим интерфейсы для предыдущих примеров:
// i nnerclasses/Desti nati on.java public interface Destination {
String readLabel(); } Hill-. 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 появится возможность оптимизировать код.
Внутренние классы в методах и областях действия