// Метод возвращает экземпляр безымянного внутреннего класса
public class Parcel7 {
public Contents contents() {
return new Contents() {// Вставить определение класса
private int i = 11;
public int value() { return i; }
}; // В данной ситуации точка с запятой необходима
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Contents c = p.contents();
}
}
Метод contents() совмещает создание возвращаемого значения с определением класса, который это возвращаемое значение и представляет! Вдобавок, этот класс является безымянным — у него отсутствует имя. Ситуация запутывается еще тем, что поначалу мы будто бы приступаем к созданию объекта Contents, а потом, остановившись перед точкой с запятой, говорим: «Стоп, а сюда я подкину определение класса».
Такая необычная форма записи значит буквально следующее: «Создать объект безымянного класса, который унаследован от Contents». Ссылка, которая возвращается при этом из выражения new, автоматически повышается до базового типа Contents. Синтаксис записи безымянного внутреннего класса является укороченной формой записи такой конструкции:
//: innerclasses/Parcel7b.java
// Расширенная версия Parcel7.java
public class Parcel7b {
class MyContents implements Contents {
private int i = 11;
public int value() { return i; }
}
public Contents contents() { return new MyContents(); }
public static void main(String[] args) {
Parcel7b p = new Parcel7b();
Contents c = p.contents();
}
}
В безымянном внутреннем классе базовый класс Contents создается с использованием конструктора по умолчанию. Следующая программа показывает, как следует поступать, если базовый класс требует вызова конструктора с аргументами:
//: innerclasses/Parcel8.java
// Вызов конструктора базового класса.
public class Parcel8 {
public Wrapping wrapping(int x) {
// Вызов конструктора базового класса:
return new Wrapping(x) { // // аргумент конструктора.
public int value() {
return super.value() * 47;
}
}; // // Требуется точка с запятой
}
public static void main(String[] args) {
Parcel8 p = new Parcel8();
Wrapping w = p.wrapping(10);
}
}
Требуемый аргумент просто передается в конструктор базового класса, как в рассмотренном примере х в выражении new Wrapping(x). Хотя это обычный класс с реализацией, Wrapping также используется в качестве общего «интерфейса» для своих производных классов:
//: innerclasses/Wrapping.java
public class Wrapping {
private int i;
public Wrapping(int x) { i = x; }
public int value() { return i; }
}
Класс Wrapping имеет конструктор с аргументом — просто для того, чтобы ситуация стала чуть более интересной. Точка с запятой в конце безымянного внутреннего класса поставлена вовсе не для того, чтобы обозначить конец тела класса (как делается в C++). Вместо этого она указывает на конец выражения, в котором содержится внутренний класс. Таким образом, в данном случае ее использование ничем не отличается от обычного. Инициализацию также можно провести в точке определения полей безымянного класса:
// Для использования в безымянном внутреннем классе
// аргументы должны быть неизменны (final)
public Destination destination(final String dest) {
return new Destination() {
private String label = dest;
public String readLabel() { return label; }
};
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination d = p.destination("Tasmania");
}
}
Если вы определяете безымянный внутренний класс и хотите при этом использовать объекты, определенные вне этого внутреннего класса, компилятор требует, чтобы переданные на них ссылки объявлялись неизменными (final), как это сделано аргументе destination(). Без такого объявления вы получите сообщение об ошибке при компиляции программы.
Пока мы ограничиваемся простым присваиванием значений полям, указанный подход работает. А если понадобится выполнить некоторые действия, свойственные конструкторам? В безымянном классе именованный конструктор определить нельзя (раз у самого класса нет имени!), но инициализация экземпляра (instance initialization) фактически позволяет добиться желаемого эффекта:
//: innerclasses/AnonymousConstructor.java
// Создание конструктора для безымянного внутреннего класса.
import static net.mindview.util.Print.*;
abstract class Base {
public Base(int i) {
print("Base constructor, i = " + i);
}
public abstract void f();
}
public class AnonymousConstructor {
public static Base getBase(int i) {
return new Base(i) {
{ print("Inside instance initializer"); }
public void f() {
print("In anonymous f()");
}
};
}
public static void main(String[] args) {
Base base = getBase(47);
base.f();
}
}
<spoiler text="Output:">
Base constructor, i = 47
Inside instance initializer
In anonymous f()
</spoiler> В таком случае переменная і не обязана быть неизменной (final). И хотя і передается базовому конструктору безымянного класса, она никогда не используется напрямую внутри безымянного класса. Вернемся к нашим объектам Parcel, на этот раз выполнив для них инициализацию экземпляра. Отметьте, что параметры метода destination() должны быть объявлены неизменными, так как они используются внутри безымянного класса: