从内部匿名类中访问外部匿名类的访问方法

24

我使用一个带有实例化另一个匿名类方法的匿名类,并且我想从这个内部匿名类中调用属于外部匿名类的方法。为了说明这一点,假设我有这个接口:

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

动机: 有人可能会问为什么我要这样做:我正在编写某种代码生成器,并希望确保我生成的代码不含歧义。


到目前为止有许多有希望的答案。它们中的大部分似乎相当合理,考虑到我没有给出比这更多的规格说明。我会再等一段时间,看看会有什么样的回答出现。 - Rulle
3个回答

20

从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 提出了另一种解决方法。你可以将你的外部对象传递给一个lambda表达式:

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

outer.getIt() 将调用 getIt(),它将创建一个新的 w,然后再次调用 outer.getIt(),再次创建一个新的 w - 创建无尽递归调用。 - SomeDude
从Java 10开始,您可以编写var outer = this;,这使您可以通过变量访问匿名外部类的新声明成员,而不仅仅是继承的成员。 - Holger
@Holger 是的,我知道。但是提问者在问题中已经提到了这个解决方法。所以我没有在我的回答中包含它。 - ETO
确实,我忽略了那个。好吧,即使是Java 8也有这种类型推断的构造。因此,您可以将w的初始化更改为ReturnsANumber w = Optional.of(this).map(outer -> new ReturnsANumber() { int theNumber() { return 1; } public int getIt() { return outer.theNumber(); } }).get();并拥有它。 - Holger
@Holger 谢谢!你的解决方案非常优雅。我已经将它添加到答案中了。 - ETO
显示剩余2条评论

15

没有访问封闭匿名类的关键字。

但是一个解决方案可以通过代理外部匿名类中的方法,并进行未限定的引用:

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's method
                }
            };

        return w.getIt();
    }
};
这种操作的必要性足以证明你的类结构并不理想,可能会成为维护陷阱。例如,你可以用一个嵌套的静态类替换第一个匿名类。

这种操作的必要性足以证明你的类结构并不理想,可能会成为维护陷阱。例如,你可以用一个嵌套的静态类替换第一个匿名类。


是的,他的类设计并不理想,我想知道为什么会生成那样的代码。但是假设可以生成那样的代码,我怀疑tgeNumberProxy()也会在类w中可用。然后它就不能工作了,它将返回1 - SomeDude
@SomeDude 这不是真的 - theNumberProxy() 是一个闭包 - 它没有任何对内部类的引用,它只能调用 v.theNumber()TIO - Delioth
@Delioth 我知道,我觉得你没有理解我的意思,问题明确表示代码是自动生成的。因此,当你生成代码时,任何对象实例都会获得相同的一组方法和字段。如果theNumberProxy()v上的一种方法,则w上也会有类似的方法。 - SomeDude

5
如果您可以扩展接口:
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());
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接