问题
The code at the end produces a compile error:
NotApplicable.java:7: run() in cannot be applied to (int)
run(42);
^
1 error
The question is why? Why does javac think I am calling run(), and does not find run(int bar)? It correctly called foo(int bar). Why do I have to use NotApplicable.this.run(42);? Is it a bug?
public class NotApplicable {
public NotApplicable() {
new Runnable() {
public void run() {
foo(42);
run(42);
// uncomment below to fix
//NotApplicable.this.run(42);
}
};
}
private void run(int bar) {
}
public void foo(int bar) {
}
}
回答1:
The explanation for the behavior of your code sample is that this
is defined to be the class that you are currently "most" inside of. In this case, you are "most" inside the anonymous inner class that subclasses runnable and there is no method which matches run(int)
. To broaden your search you specify which this
you want to use by stating NotApplicable.this.run(42)
.
The jvm will evaluate as follows:
this
-> currently executing instance of Runnable
with method run()
NotApplicable.this
-> currently executing instance of NotApplicable
with method run(int)
The compiler will look up the nesting tree for the first method that matches the NAME of the method. –Thanks to DJClayworth for this clarification
The anonymous inner class is not a subclass of the outer class. Because of this relationship, both the inner class and the outer class should be able to have a method with exactly the same signature and the innermost code block should be able to identify which method it wants to run.
public class Outer{
public Outer() {
new Runnable() {
public void printit() {
System.out.println( "Anonymous Inner" );
}
public void run() {
printit(); // prints "Anonymous Inner"
this.printit(); //prints "Anonymous Inner"
// would not be possible to execute next line without this behavior
Outer.this.printit(); //prints "Outer"
}
};
}
public void printit() {
System.out.println( "Outer" );
}
}
回答2:
As far as I recall the rules for selecting a method to run between nested classes are approximately the same as the rules for selecting a method in an inheritance tree. That means that what we are getting here is not overloading, it's hiding. The difference between these is crucial to understanding methods in inheritance.
If your Runnable was declared as a subclass, then the run() method would hide the run(int) method in the parent. Any call to run(...) would try to execute the one on Runnable, but would fail if it couldn't match signatures. Since foo is not declared in the child then the one on the parent is called.
The same principle is happening here. Look up references to "method hiding" and it should be clear.
回答3:
This is because run
is being re-declared when you enter the new Runnable() {}
scope. All previous bindings to run become inaccessible. It's as if you were doing this:
import java.util.*;
public class tmp
{
private int x = 20;
public static class Inner
{
private List x = new ArrayList();
public void func()
{
System.out.println(x + 10);
}
}
public static void main(String[] args)
{
(new Inner()).func();
}
}
The compiler won't look for something that matches the type of x
all the way up the scope stack, it'll just halt when it finds the first references and sees that the types are incompatible.
NOTE: It's not as if it couldn't do this... it's just that, to preserve your own sanity, it's been decided that it shouldn't.
来源:https://stackoverflow.com/questions/252267/why-cant-i-call-a-method-outside-of-an-anonymous-class-of-the-same-name