24

I instantiate an anonymous class with a method that instantiates another anonymous class, and from this inner anonymous class I want to call a method belonging to the outer anonymous class. To illustrate it, suppose I have this interface:

interface ReturnsANumber {
    int getIt();
}

And then, somewhere in my code, I do this:

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

Question: In the innermost method getIt, I want to call theNumber() belonging to the outermost anonymous class. How can I accomplish that without using the Java 10 var feature (as hinted in the code).

Clarification: Ideally, the outer anonymous class should not need to know that the inner class wants to call its theNumber method. The idea is to come up with some code that lets the inner class unambiguously call any method on the outer class.

In other words, how can I make this code display: The number is 119 (instead of displaying The number is 1)

Motivation: Someone might ask why I want to do this anyway: I am writing some sort of code generator and want to be sure that the code that I am generating is not ambiguous.

ETO
  • 6,970
  • 1
  • 20
  • 37
Rulle
  • 4,496
  • 1
  • 15
  • 21
  • Many promising answers so far. Most of them seem pretty reasonable given that I didn't give more specifications than this. I will wait a bit more to see what kind of answers come up. – Rulle Nov 08 '18 at 16:57

3 Answers3

20

Since Java 8 the solution is pretty easy. Just store the method reference in a variable.

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

Storing outer object could also do the trick. But for inherited methods only:

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

You can store the method reference or the outer object as a field also.

Update

@Holger proposed another workaround. You can pass your outer object to a 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();
    }
};
ETO
  • 6,970
  • 1
  • 20
  • 37
  • `outer.getIt()` will call `getIt()` it will create a new `w` and then call again `outer.getIt()` , again creates a new `w` - creating endless recursive calls. – SomeDude Nov 08 '18 at 17:01
  • @SomeDude As I mentioned, it will work `for inherited methods only`. Interface `ReturnsANumber` doesn't have such method. – ETO Nov 08 '18 at 17:07
  • He might have meant to call outer.theNumber() within the inner class' getIt() – Nicolás Marzano Nov 08 '18 at 18:47
  • Starting with Java 10, you can write `var outer = this;`, which allows you to access newly declared members of the anonymous outer class through the variable, not only inherited ones. – Holger Nov 19 '18 at 09:02
  • @Holger Yes, I know. But OP mentioned this solution in his question. So I didn't include it to my answer. – ETO Nov 19 '18 at 09:05
  • Indeed, I overlooked that. Well, even Java 8 has constructs with such type inference. So you can change the initialization of `w` to `ReturnsANumber w = Optional.of(this).map(outer -> new ReturnsANumber() { int theNumber() { return 1; } public int getIt() { return outer.theNumber(); } }).get();` and have it. – Holger Nov 19 '18 at 09:13
  • @Holger Thanks! Your solution is very elegant. I've added it to the answer. – ETO Nov 19 '18 at 09:55
15

There is no keyword for accessing the enclosing anonymous class.

But one solution could be proxying the method in the outer anonymous class and making an unqualified reference:

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

The need for such manoeuvre should be proof enough that your class structure is not ideal and could be a maintenance trap. You could simply replace the first anonymous class with a nested static class, for example.

ernest_k
  • 44,416
  • 5
  • 53
  • 99
  • Yes his class design is not ideal and I wonder why would generate code like that. But lets assume one could generate code like that, I am suspecting `theNumberProxy()` would also be available in the class `w` . Then it won't work. It would return `1`. – SomeDude Nov 08 '18 at 16:48
  • @SomeDude That's not true - `theNumberProxy()` is a closure - it doesn't have any reference to the inner class, it can only call `v.theNumber()`. [TIO](https://tio.run/##hVLBToQwED3DV8xNOAjZoyEePGqiMe7ReOjCAF2hJe0U3Bi@HdtlQcDE7aXpzLw38970yFp2KxsUx@xzGLggVDlLEd6QjBL64cXUB1Tw7YM9Ng1U4hgLwmQOFkiP5AK976cV0xqeGRcW5XuNOVQ8BU2M7NVKnkFtc8GeFBfF@wcwVejQVXqbni3cg8BuM0oQWlrPm3g3I405T50hsNvdJe7dO3Yvjp@MJmCQ8TxHhRYpWI2QS7WkcKUr1lclv05baiq5jlZezH0Wk118uUA3@rp/9V1R@KsxGd/9H9C6@WLyta4E4jhlVaUBRVpJbbcC5x3eaKiRSpktG/TJ0oUumjc/ybcFvvPamQ@ZQSAJjtYo1DaxP2nCOpKGosZ@AKpE0E4kjqW3X2gYfgA) – Delioth Nov 08 '18 at 18:18
  • @Delioth I know , I think you didn't understand what I said, the question specifically said the code is generated. So when you generate code, any instance of an object would get the same set of methods, fields. If `theNumberProxy()` is a method on `v` there would also be a similar method on `w`. – SomeDude Nov 08 '18 at 18:45
5

If you can extend the interface:

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());
    }
}
Ortwin Angermeier
  • 5,957
  • 2
  • 34
  • 34