views:

501

answers:

3

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) {
    }
}
A: 

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.

Claudiu
but the two run function dont have the same signature. This is overloading. Not overriding.
Frederic Morin
apparently, it is overriding it. If it wasn't, then it would overload it correctly.
Claudiu
It's hiding not overloading.
DJClayworth
+6  A: 

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" );
    }
}
Alex B
You don't explain why foo(int bar) is correctly called.
Pyrolistical
This answer is correct, except for the part about "This behavior would be exhibited even if both methods were not named 'run'". The compiler will look up the nesting tree for the first method that matches the NAME of the method.
DJClayworth
+1  A: 

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.

DJClayworth