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.
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.
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.
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.
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();
}
};
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.
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());
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With