Метод доступа к внешнему анонимному классу из внутреннего анонимного класса

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

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());
    }
}