Вложенные классы
В этой главе уже несколько раз упоминалось, что в теле класса можно сделать описание другого, вложенного (nested) класса. А во вложенном классе можно снова описать вложенный, внутренний (inner) класс и т. д. Эта матрешка кажется вполне естественной, но вы уже поднаторели в написании классов, и у вас возникает масса вопросов.
- Можем ли мы из вложенного класса обратиться к членам внешнего класса? Можем, для того это все и задумывалось.
- А можем ли мы в таком случае определить экземпляр вложенного класса, не определяя экземпляры внешнего класса? Нет, не можем, сначала надо определить хоть один экземпляр внешнего класса, матрешка ведь!
- А если экземпляров внешнего класса несколько, как узнать, с каким экземпляром внешнего класса работает данный экземпляр вложенного класса? Имя экземпляра вложенного класса уточняется именем связанного с ним экземпляра внешнего класса. Более того, при создании вложенного экземпляра операция new тоже уточняется именем внешнего экземпляра.
- А…?
Хватит вопросов, давайте разберем все по порядку.
Все вложенные классы можно разделить на вложенные классы-члены класса (member classes), описанные вне методов, и вложенные локальные классы (local classes), описанные внутри методов и/или блоков. Локальные классы, как и все локальные переменные, не являются членами класса.
Классы-члены могут быть объявлены статическим модификатором static. Поведение статических классов-членов ничем не отличается от поведения обычных классов, отличается только обращение к таким классам. Поэтому они называются вложенными классами верхнего уровня (nestee tep-level classes), хотя статические классы-члены можно вкладывать друг в друга. В них можно объявлять статические члены. Используются они обычно для того, чтобы сгруппировать вспомогательные классы вместе с основным классом.
Все нестатические вложенные классы называются внутренними (inner). В них нельзя объявлять статические члены.
Локальные классы, как и все локальные переменные, известны только в блоке, в котором они определены. Они могут быть безымянными (anonymous classes).
В листинге 2.7 рассмотрены все эти случаи.
Листинг 2.7. Вложенные классы.
class Nested{ static private int pr; // Переменная pr обьявленa статической // чтобы к ней был доступ из статических классов А и АВ String s = "Member of Nested"; // Вкладываем статический класс. static class.А{ // Полное имя этого класса – Nested.A private int a=pr; String s = "Member of A"; // Во вложенньм класс А вкладываем еще один статический класс static class AB{ // Полное имя класса – Nested.А.АВ private int ab=pr; String s = "Member of AB"; } } //В класс Nested вкладываем нестатический класс class В{ // Полное имя этого класса – Nested.В private int b=pr; String s = "Member of B"; // В класс В вкладываем еще один класс class ВС{ // Полное имя класса – Nested.В.ВС private int bc=pr; String s = "Member of ВС"; } void f(final int i){ // Без слова final переменные i и j final int j = 99; // нельзя использовать в локальном классе D class D{ // Локальный класс D известен только внутри f() private int d=pr; String s = "Member of D"; void pr(){ // Обратите внимание на то, как различаются // переменные с одним и тем же именем "s" System.out.println(s + (i+j)); // "s" эквивалентно "this.s" System.out.println(B.this.s); System.out.println(Nested.this.s); // System.out.println(AB.this.s); // Нет доступа // System.out.println(A.this.s); // Нет доступа } } D d = new D(); // Объект определяется тут же, в методе f() d.pr(); // Объект известен только в методе f() } } void m(){ new Object(){ // Создается объект безымянного класса, // указывается конструктор его суперкласса private int e = pr; void g(){ System.out.println("From g()); } }.g(); // Тут же выполняется метод только что созданного объекта } } public class NestedClasses{ public static void main(String[] args){ Nested nest = new Nested(); // Последовательно раскрываются // три матрешки Nested.A theA = nest.new A(); // Полное имя класса и уточненная // операция new. Но конструктор только вложенного класса Nested.A.AB theAB = theA.new AB(); // Те же правила. Операция // new уточняется только одним именем Nested.В theB = nest.new B(); // Еще одна матрешка Nested.В.ВС theBC = theB.new BC(); theB.f(999); // Методы вызываются обычным образом nest.m(); } }