Философия Java

Анонимный внутренний класс


Следующий пример несколько странен:

//: c08:Parcel6.java

// Метод возвращающий анонимный внутренний класс.

public class Parcel6 { public Contents cont() { return new Contents() { private int i = 11; public int value() { return i; } }; // В этом случае требуется точка с запятой

} public static void main(String[] args) { Parcel6 p = new Parcel6(); Contents c = p.cont(); } } ///:~

Метод cont( ) комбинирует создание возвращаемого значения с описанием класса, который и есть это возвращаемое значение! В дополнение этот класс еще и не имеет своего имени. Делая тему обсуждения немного запутанной, он выглядит как будто Вы начинаете создавать объект Contents:

return new Contents()

Но затем, до того, как Вы поставите точку запятую, Вы заявляете: "Но подождите, я думаю, я описался в определении класса":

return new Contents() { private int i = 11; public int value() { return i; } };

А вот, что означает этот синтаксис: "Создание объекта анонимного класса, который наследует от Contents." Ссылка, возвращаемая выражением new, автоматически приводится к базовому типу, к ссылке Contents. Синтаксис анонимного внутреннего класса - короткая запись следующего кода:

class MyContents implements Contents { private int i = 11; public int value() { return i; } } return new MyContents();

В анонимном внутреннем классе, Contents создается при помощи конструктора по умолчанию. Следующий же код показывает, как поступить, если нужно создать его с помощью конструктора с аргументами:

//: c08:Parcel7.java

// Анонимный внутренний класс вызывающий

// конструткор базового класса.

public class Parcel7 { public Wrapping wrap(int x) { // Вызов базового конструктора:

return new Wrapping(x) { public int value() { return super.value() * 47; } }; // Требуется точка с запятой

} public static void main(String[] args) { Parcel7 p = new Parcel7(); Wrapping w = p.wrap(10); } } ///:~



То есть, Вы просто передаете соответствующий аргумент в конструктор базового класса, конкретно здесь x передается в new Wrapping(x). Анонимный класс не может иметь конструктор, в котором Вы могли бы нормально вызвать super( ).


В обоих предыдущих примерах, точка с запятой не означает конец тела класса (как в C++). Вместо этого, они показывают конец выражения, которое содержит анонимный класс. Таким образом, это равносильно использованию точки запятой где нибудь еще (т.е. они имеют то же значение, что и в любом другом месте).

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

//: c08:Parcel8.java

// Анонимный внутренний класс осуществляющий

// инициализацию. Краткая версия

// Parcel5.java.

public class Parcel8 { // Аргумент должен быть final для использования внутри

// анонимного внутреннего класса:

public Destination dest(final String dest) { return new Destination() { private String label = dest; public String readLabel() { return label; } }; } public static void main(String[] args) { Parcel8 p = new Parcel8(); Destination d = p.dest("Tanzania"); } } ///:~

Если Вы определяете анонимный внутренний класс и хотите использовать в нем объект, который определен снаружи этого анонимного внутреннего класса, то тогда компилятор потребует, что бы данный объект был объявлен, как final. Вот поэтому-то аргумент dest( ) - final. Если же Вы забыли, в противном случае появляется ошибка времени выполнения.

Как только Вы научились получать доступ к объектам вне внутреннего класса, так сразу же возникает вопрос, а что делать, если нужно осуществить некое действие похожее на конструктор При помощи инициализации экземпляра, Вы можете, в действительности, создать конструткор для анонимного внутреннего класса:

//: c08:Parcel9.java

// Использование "инициализации экземпляра" для осуществления

// создания анонимного внутреннего класса.

public class Parcel9 { public Destination dest(final String dest, final float price) { return new Destination() { private int cost; // Экземплярная инициализация для каждого объекта:

{ cost = Math.round(price); if(cost > 100) System.out.println("Over budget!"); } private String label = dest; public String readLabel() { return label; } }; } public static void main(String[] args) { Parcel9 p = new Parcel9(); Destination d = p.dest("Tanzania", 101.395F); } } ///:~

Внутри инициализатора Вы можете видеть код, который не будет исполнен как часть инициализатора полей (т.е., выражение if). На самом же деле, инициализатор экземпляра по своей сути есть ничто иное, чем конструктор анонимного класса. Разумеется, что он несколько ограничен; Вы не можете перегрузить инциализатор экземпляра, поэтому у вас есть только один такой "конструктор".


Содержание раздела