web-dev-qa-db-ja.com

内部匿名クラスから外部匿名クラスへのアクセス方法

別の匿名クラスをインスタンス化するメソッドを使用して匿名クラスをインスタンス化し、この内部匿名クラスから、外部匿名クラスに属するメソッドを呼び出します。それを説明するために、私がこのインターフェースを持っていると仮定します:

_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 10var)機能を使用せずにそれを実現するにはどうすればよいですか(コードで示唆)。

説明:理想的には、外部の匿名クラスは、内部クラスがそのtheNumberメソッドを呼び出したいことを知っている必要はありません。アイデアは、内部クラスが外部クラスのanyメソッドを明確に呼び出すことができるいくつかのコードを考え出すことです。

つまり、このコードを表示するにはどうすればよいですか:_The number is 119_(_The number is 1_を表示する代わりに)

モチベーション:なぜとにかくこれをしたいのかと誰かが尋ねるかもしれません:私はある種のコードジェネレーターを書いていて、生成しているコードがあいまいでないことを確認したいと思います。

22
Rulle

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は別の回避策を提案しました。 outerオブジェクトをラムダに渡すことができます:

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

囲んでいる匿名クラスにアクセスするためのキーワードはありません。

しかし、1つの解決策は、外部の匿名クラスでメソッドをプロキシし、修飾されていない参照を作成することです。

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

このような操作の必要性は、クラス構造が理想的ではなく、メンテナンスの罠になる可能性があることを証明するものでなければなりません。たとえば、最初の匿名クラスをネストされた静的クラスに置き換えるだけです。

13
ernest_k

インターフェースを拡張できる場合:

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