Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access method of outer anonymous class from inner anonymous class

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.

like image 971
Rulle Avatar asked Nov 08 '18 16:11

Rulle


People also ask

Can inner class access outer class method?

Method Local inner classes can't use a local variable of the outer method until that local variable is not declared as final. For example, the following code generates a compiler error.

How do you refer to outer class from inner class?

If you want your inner class to access outer class instance variables then in the constructor for the inner class, include an argument that is a reference to the outer class instance. The outer class invokes the inner class constructor passing this as that argument.

What is the difference between anonymous class and inner class?

A local inner class consists of a class declared within a method, whereas an anonymous class is declared when an instance is created. So the anonymous class is created on the fly or during program execution.


3 Answers

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();
    }
};
like image 100
ETO Avatar answered Sep 28 '22 12:09

ETO


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.

like image 24
ernest_k Avatar answered Sep 28 '22 12:09

ernest_k


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());
    }
}
like image 30
Ortwin Angermeier Avatar answered Sep 28 '22 10:09

Ortwin Angermeier