29

29


•       для создания объекта статического внутреннего класса не нужен объект внешнего класса;

•       из объекта вложенного класса нельзя обращаться к не-статическим чле­нам внешнего класса.

Есть и еще одно различие между вложенными и обычными внутренними классами. Поля и методы обычного внутреннего класса определяются только на уровне внешнего класса, поэтому обычные внутренние классы не могут со­держать статические данные, поля и классы. Но вложенные классы не имеют таких ограничений:

//• i nnerclasses/Parcel 11.java

// Вложенные (статические внутренние) классы

public class Parcel 11 {

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

}

protected static class ParcelDestination implements Destination { private String label;

private Parcel Destination(String whereTo) { label = whereTo,

}

public String readLabelO { return label; }

// Вложенные классы могут содержать другие статические элементы;

public static void f() {}

static int x = 10;

static class AnotherLevel {

public static void f() {} static int x = 10;

}

}

public static Destination destination(String s) { return new ParcelDestination(s);

}

public static Contents contO {

return new Parcel Contents О,

}

public static void main(String[] args) { Contents с = contentsO, Destination d = destinationC'TacMaHHfl"),

}

} ///;-

В методе main() не требуется объекта класса Parcelll; вместо этого для вызова методов, возвращающих ссылки на Contents и Destination, используется обыч­ный синтаксис обращения к статическим членам класса.

Как было сказано ранее, в обычном (не-статическом) внутреннем классе для обращения к объекту внешнего класса используется специальная ссылка this. Во вложенном классе такая ссылка недействительна (по аналогии со статиче­скими методами).

Классы внутри интерфейсов

Обычно интерфейс не может содержать программный код, но вложенный классможетстать его частью. Любой класс, размещенный внутри интерфейса, авто­матически является public и static. Так как класс является статическим, он не на­рушает правил обращения с интерфейсом — этот вложенный класс просто ис­пользует пространство имен интерфейса. Во внутреннем классе даже можно реализовать окружающий интерфейс:

//• innerclasses/ClassInlnterface.java // {main: ClassInlnterfaceSTest}

public interface Classlnlnterface { void howdyO;

class Test implements Classlnlnterface { public void howdyO {

System. out.printlnCnpHBeT!");.

}

public static void main(String[] args) { new Test О .howdyO.

}

}

} /* Output Привет! *///•-

Вложение классов в интерфейсы может пригодиться для создания обобщен­ного кода, используемого с разными реализациями этого интерфейса.

Ранее в книге я предлагал помещать в каждый класс метод main(), позволяю­щий при необходимости протестировать данный класс. Недостатком такого подхода является дополнительный скомпилированный код, увеличивающий размеры программы. Если для вас это нежелательно, используйте статический внутренний класс для хранения тестового кода:

//• innerclasses/TestBed.java

// Помещение тестового кода во вложенный класс

// {main: TestBedSTester}

public class TestBed {

public void f() { System.out.printlnC'fO"): } public static class Tester {

public static void main(String[] args) { TestBed t = new TestBedO; t.fO:

}

}

} /* Output: f() *///:-

При компиляции этого файла создается отдельный класс с именем TestBed$ Tester (для запуска тестового кода наберите команду java TestBed$Tester). Вы мо­жете использовать этот класс для тестирования, но включать его в окончатель­ную версию программы необязательно; файл TestBed$Tester.class можно просто удалить перед окончательной сборкой программы.

Доступ вовне из многократно вложенных классов

Независимо от глубины вложенности, внутренний класс всегда может нап­рямую обращаться ко всем членам всех классов, в которые он встроен. Следую­щая программа демонстрирует этот факт
1
:

//: innerclasses/MultiNestingAccess.java // Вложенные классы могут обращаться ко всем членам всех // классов, в которых они находятся.

class MNA {

private void f() {} class A {

private void g() {} public class В {

void h() {

g();

f():

}

}

}

}

public class MultiNestingAccess {

public static void main(String[] args) { MNA mna = new MNA(); MNA.A mnaa = mna.new AO; MNA.А.В mnaab = mnaa.new BO; mnaab h();

}

} ///.-

Как видно из примера, в классе MNA.A.B методы f() и д() вызываются без до­полнительных описаний (несмотря на то, что они объявлены как private). Этот пример также демонстрирует синтаксис, который следует использовать при создании объектов внутренних классов произвольного уровня вложенности из другого класса. Синтаксис .new обеспечивает правильную область действия, и вам не приходится уточнять имя класса при вызове конструктора.

Внутренние классы: зачем?

К настоящему моменту мы подробно рассмотрели синтаксис и семантику рабо­ты внутренних классов, но это не дало ответа на вопрос, зачем они вообще нужны.

Что же заставило создателей Java добавить в язык настолько фундаментальное свойство?

Обычно внутренний класс наследует от класса или реализует интерфейс, а код внутреннего класса манипулирует объектом внешнего класса, в котором он был создан. Значит, можно сказать, что внутренний класс — это нечто вроде «окна» во внешний класс.

Возникает резонный вопрос: «Если мне понадобится ссылка на интерфейс, почему бы внешнему классу не реализовать этот интерфейс?» Ответ: «Если это все, что вам нужно, — значит, так и следует поступить». Но что же отличает внутренний класс, реализующий интерфейс, от внешнего класса, реализующего тот же интерфейс? Далеко не всегда удается использовать удобство интерфей­сов — иногда приходится работать и с реализацией. Поэтому наиболее веская причина для использования внутренних классов такова:

Каждый внутренний класс способен независимо наследовать определенную реализацию. Таким образом, внутренний класс не ограничен при наследовании в ситуациях, где внешний класс уже наследует реализацию.

Без возможности внутренних классов наследовать реализацию более чем од­ного реального или абстрактного класса некоторые задачи планирования и про­граммирования становятся практически неразрешимыми. Поэтому внутренний класс выступает как «довесок» решения проблемы множественного наследова­ния. Интерфейсы берут на себя часть этой задачи, тогда как внутренние классы фактически обеспечивают «множественное наследование реализации». Други­ми словами, внутренние классы позволяют наследовать от нескольких не-ин- терфейсов.