Представление вложенных и внутренних классов

Java позволяет определять класс внутри другого класса. Такие классы называют вложенными классами. Область определения вложенного класса ограничена областью определения внешнего класса. Таким образом, если класс В определен внутри класса А, класс В не может существовать независимо от класса .А- Вложенный класс имеет доступ к членам, в том числе приватным, класса, в который он вложен. Однако внешний класс не имеет доступ к членам вложенного класса. Вложенный класс, который объявлен непосредственно внутри области определения своего внешнего класса, является его членом. Можно также объявлять вложенные классы, являющиеся локальными для блока.

Существует два типа вложенных классов: статические и нестатические. Статический вложенный класс — класс, к которому применен модификатор static. Поскольку он является статическим, он должен обращаться к своему внешнему классу посредством объекта. То есть он не может непосредственно ссылаться на члены своего внешнего класса. Из-за этого ограничения статические вложенные классы используются редко.

Наиболее важный тип вложенного класса — внутренний класс. Внутренний класс — это нестатический вложенный класс. Он имеет доступ ко всем переменным и методам своего внешнего класса и может непосредственно ссылаться на них так же, как это делают остальные нестатические члены внешнего класса.

Следующая программа иллюстрирует определение и использование внутреннего класса. Класс Outer содержит одну переменную экземпляра outer_x, один метод экземпляра test () и определяет один внутренний класс Inner.

// Демонстрация использования внутреннего класса.
class Outer {
int outer_x = 100; void test() {
Inner inner = new Inner ();
inner.display() ;
}
// это внутренний класс
class Inner {
void display () {
System.out.println("вывод: outer_x = " + outer_x);
}
}
}
class InnerClassDemo {
public static void main(String args[]) {
Outer outer = new Outer ();
outer.test () ;
}
}

Это приложение генерирует следующий вывод:

вывод: outer_x = 100

В этой программе внутренний класс Inner определен в области определения класса Outer. Поэтому любой кода в классе Inner может непосредственно обращаться к переменной outer_x. Метод экземпляра display () определен внутри класса Inner. Этот метод отображает значение переменной outer_x в стандартном выходном потоке. Метод main () экземпляра InnerClassDemo создает экземпляр класса Outer и вызывает его метод test (). Этот метод создает экземпляр класса Inner и вызывает метод display ().

Важно понимать, что экземпляр класса Inner может быть создан только внутри области определения класса Outer. Компилятор Java генерирует сообщение об ошибке, если любой код вне класса Outer пытается инициализировать класс Inner. (В общем случае экземпляр внутреннего класса должен создаваться содержащим его диапазоном.) Однако экземпляр класса Inner можно создать снаружи класса Outer, уточняя имя внутреннего класса именем внешнего класса — например Outer. Inner.

Как уже было сказано, внутренний класс имеет доступ ко всем элементам своего внешнего класса, но не наоборот. Члены внутреннего класса известны только внутри области определения внутреннего класса и не могут быть использованы внешним классом. Например:

// Компиляция этой программы будет не возможна.
class Outer {
int outer_x = 100;
void test () {
Inner inner = new Inner ();
inner.display();
}
// это внутренний класс class Inner {
int у = 10;
//у — локальная переменная класса
Inner void display() {
System.out.println("вывод: outer_x = " + outer_x);
void showy () {
System.out.println(у); // ошибка; здесь переменная у не известна!
}
}
class InnerClassDemo {
public static void main(String args[]) {
Outer outer = new Outer () ;
outer.test ();
}

В этом примере переменная у объявлена как переменная экземпляра класса Inner. Поэтому она не известна за пределами класса и не может использоваться методом showy().

Хотя мы уделили основное внимание внутренним классам, определенным в качестве членов внутри области определения внешнего класса, внутренние классы можно определять внутри области определения любого блока. Например, вложенный класс можно определить внутри блока, определенного методом, или даже внутри тела цикла for, как показано в следующем примере:

// Определение внутреннего класса внутри цикла for.
class Outer {
int outer_x = 100; void test () {
for (int i=0; i<10; i++) {
class Inner {
void display() {
System.out.println("вывод: outer_x = " + outer_x);
}
}
Inner inner = new Inner ();
inner.display();
}
}
}
class InnerClassDemo {
public static void main(String args[]) {
Outer outer = new Outer ();
outer.test ();
}
}

Вывод, генерируемый этой версией программы, показан ниже.

вывод: outer X = 100
вывод: outer X = 100
вывод: outer X = 100
вывод: outer X = 100
вывод: outer X = 100
вывод: outer X = 100
вывод: outer X = 100
вывод: outer X = 100
вывод: outer X = 100
вывод: outer X = 100

Хотя вложенные классы применимы не во всех ситуациях, они особенно удобны при обработке событий. Мы вернемся к теме вложенных классов в главе 22. В ней представлены внутренние классы, которые можно использовать для упрощения кода, предназначенного для обработки определенных типов событий. Читатели ознакомятся также с анонимными внутренними классами, являющимися внутренними классами без имен.

И последнее: первоначальная спецификация Java версии 1.0 не допускала использования вложенных классов. Они появились в версии Java 1.1.