Метод доступа к внешнему анонимному классу из внутреннего анонимного класса
Я создаю экземпляр анонимного класса с помощью метода, который создает экземпляр другого анонимного класса, и из этого внутреннего анонимного класса я хочу вызвать метод, принадлежащий внешнему анонимному классу. Чтобы проиллюстрировать это, предположим, у меня есть этот интерфейс:
interface ReturnsANumber {
int getIt();
}
И затем, где-то в моем коде, я делаю это:
ReturnsANumber v = new ReturnsANumber() {
int theNumber() {
return 119;
}
public int getIt() {
// In a modern version of Java, maybe I could do
// var a = this;
// and then call a.theNumber();
ReturnsANumber w = new ReturnsANumber() {
int theNumber() {
return 1;
}
public int getIt() {
return this.theNumber();
}
};
return w.getIt();
}
};
System.out.println("The number is " + v.getIt());
Вопрос: В самом внутреннем методе getIt
я хочу вызвать theNumber()
принадлежащий внешнему анонимному классу. Как я могу сделать это без использования функции Java 10 var (как подсказано в коде).
Пояснение: в идеале внешний анонимный класс не должен знать, что внутренний класс хочет вызвать его метод theNumber
. Идея состоит в том, чтобы придумать некоторый код, который позволяет внутреннему классу однозначно вызывать любой метод внешнего класса.
Другими словами, как я могу заставить этот код отображать: The number is 119
(вместо отображения The number is 1
)
Мотивация: Кто-то может спросить, почему я все равно хочу это сделать: я пишу какой-то генератор кода и хочу быть уверен, что код, который я генерирую, не является неоднозначным.
Ответы
Ответ 1
Начиная с Java 8 решение довольно простое. Просто сохраните ссылку на метод в переменной.
ReturnsANumber v = new ReturnsANumber() {
int theNumber() {
return 119;
}
public int getIt() {
Supplier<Integer> supplier = this::theNumber;
ReturnsANumber w = new ReturnsANumber() {
int theNumber() {
return 1;
}
public int getIt() {
return supplier.get();
}
};
return w.getIt();
}
};
Хранение внешнего объекта также может помочь. Но только для унаследованных методов:
interface ReturnsANumber {
int theNumber();
int getIt();
}
public int getIt() {
ReturnsANumber outer = this;
ReturnsANumber w = new ReturnsANumber() {
public int theNumber() {
return 1;
}
public int getIt() {
return outer.theNumber();
}
};
return w.getIt();
}
Вы также можете сохранить ссылку на метод или внешний объект как поле.
Обновить
@Holger предложил другой обходной путь. Вы можете передать свой внешний объект лямбде:
ReturnsANumber v = new ReturnsANumber() {
...
@Override
public int getIt() {
ReturnsANumber w = Optional.of(this).map(outer ->
new ReturnsANumber() {
int theNumber() {
return 1;
}
public int getIt() {
return outer.theNumber();
}
}).get();
return w.getIt();
}
};
Ответ 2
Ключевого слова для доступа к анонимному классу нет.
Но одним из решений может быть проксирование метода во внешнем анонимном классе и создание неквалифицированной ссылки:
ReturnsANumber v = new ReturnsANumber() {
int theNumber() {
return 119;
}
//Just a different name for theNumber()
int theNumberProxy() {
return theNumber();
}
public int getIt() {
ReturnsANumber w = new ReturnsANumber() {
int theNumber() {
return 1;
}
public int getIt() {
return theNumberProxy(); //calls enclosing class method
}
};
return w.getIt();
}
};
Необходимость такого маневра должна быть достаточным доказательством того, что структура вашего класса не идеальна и может стать ловушкой для обслуживания. Например, вы можете просто заменить первый анонимный класс на вложенный статический класс.
Ответ 3
Если вы можете расширить интерфейс:
public class Test {
interface ReturnsANumber {
int theNumber();
int getIt();
}
public static void main(String[] args) {
ReturnsANumber v = new ReturnsANumber() {
public int theNumber() {
return 119;
}
public int getIt() {
final ReturnsANumber that = this;
// In a modern version of Java, maybe I could do
// var a = this;
// and then call a.theNumber();
ReturnsANumber w = new ReturnsANumber() {
public int theNumber() {
return 1;
}
public int getIt() {
return that.theNumber();
}
};
return w.getIt();
}
};
System.out.println("The number is " + v.getIt());
}
}