Эккель Брюс
Шрифт:
}
} /* Output New EggO Egg. YolkO *///•-
Конструктор по умолчанию автоматически синтезируется компилятором, а в нем вызывается конструктор по умолчанию из базового класса. Можно подумать, что при создании объекта BigEgg должен использоваться «переопределенный» класс Yolk, но это отнюдь не так, как видно из результата работы программы.
Этот пример просто показывает, что при наследовании от внешнего класса ничего особенного с внутренними классами не происходит. Два внутренних класса — совершенно отдельные составляющие, с независимыми пространствами имен. Впрочем, возможность явного наследования от внутреннего класса сохранилась:
//: innerclasses/BigEgg2.java
// Правильное наследование внутреннего класса,
i mport stati с net.mi ndvi ew.uti1.Pri nt.*;
class Egg2 {
protected class Yolk {
public YolkO { print("Egg2.YolkO"): } public void f {
print("Egg2 Yolk.fO"):}
}
private Yolk у = new YolkO, продолжение &
public Egg2 { print("New Egg2"); } public void insertYolk(Yolk yy) { у = yy; } public void g { y.f; }
}
public class BigEgg2 extends Egg2 {
public class Yolk extends Egg2 Yolk {
public YolkO { print("BigEgg2.Yolk"); }
public void f { System.out.println("BigEgg2.Yolk.f"); }
}
public BigEgg2 { insertYolk(new YolkO); } public static void main(String[] args) { Egg2 e2 = new BigEgg2; e2.g;
}
} /* Output: Egg2. YolkO New Egg2 Egg2. YolkO BigEgg2. YolkO BigEgg2.Yolk.f *///•-
Теперь класс BigEgg2.Yolk явно расширяет класс Egg2.Yolk и переопределяет его методы. Метод insertYolk позволяет классу BigEgg2 повысить один из своих объектов Yolk до ссылки у в классе Egg2, поэтому при вызове y.f в методе д используется переопределенная версия f. Второй вызов Egg2.Yolk — это вызов конструктора базового класса из конструктора класса BigEgg2.Yolk. Мы также видим, что при вызове метода д используется «обновленная» версия мето-даЮ.
Локальные внутренние классы
Как было замечено ранее, внутренние классы также могут создаваться в блоках кода — чаще всего в теле метода. Локальный внутренний класс не может иметь спецификатора доступа, так как он не является частью внешнего класса, но для него доступны все неизменные (final) переменные текущего блока и все члены внешнего класса. Следующий пример сравнивает процессы создания локального внутреннего класса и безымянного внутреннего класса:
//: innerclasses/LocalInnerClass.java // Хранит последовательность объектов, import static net.mindview.util.Print.*;
interface Counter { int nextO;
}
public class LocalInnerClass { private int count = 0; Counter getCounter(final String name) { // Локальный внутренний класс: class Local Counter implements Counter { public Local CounterО {
return new Local CounterО;
}
// To же самое с безымянным внутренним классом: Counter getCounter2(final String name) { return new CounterO {
// У безымянного внутреннего класса не может быть // именованного конструктора, «легальна» только
// инициализация экземпляром: {
print ("Counter О");
}
public int next О {
printnb(name): // неизменный аргумент return count++:
}
public static void main(String[] args) {
LocalInnerClass lie = new LocalInnerClassO: Counter
cl = lic.getCounter(" локальный"), c2 = lic.getCounter2(" безымянный"): for(int i = 0: i < 5: i++) print(cl.nextO): for(int i = 0: i < 5: i++) print(c2.next);
}
}
} /* Output: Local CounterO CounterO локальный 0 локальный 1 локальный 2 локальный 3 локальный 4 безымянный 5 безымянный 6 безымянный 7 безымянный 8 безымянный 9 *///:-
Объект Counter возвращает следующее по порядку значение. Он реализован и как локальный класс, и как безымянный внутренний класс, с одинаковым поведением и характеристиками. Поскольку имя локального внутреннего класса недоступно за пределами метода, доводом для применения локального класса вместо безымянного внутреннего может быть необходимость в именованном конструкторе и (или) перегруженных конструкторах; безымянные внутренние классы допускают только инициализацию экземпляром.
// У локального внутреннего класса // может быть собственный конструктор: pri nt("Local Counter");
}
public int next О {
printnb(name): // неизменный аргумент return count++;
Другая причина для использования локального внутреннего класса вместо безымянного внутреннего — необходимость создания более чем одного объекта такого класса.
Идентификаторы внутренних классов
Так как каждый класс компилируется в файл с расширением .class, содержащий полную информацию о создании его экземпляров (эта информация помещается в «мета-класс», называемый объектом Class), напрашивается предположение, что внутренние классы также создают файлы .class для хранения информации о своих объектах Class. Имена этих файлов-классов строятся по жестко заданной схеме: имя объемлющего внешнего класса, затем символ $ и имя внутреннего класса. Например, для программы LocallnnerClass.java создаются следующие файлы с расширением .class: