Почему я не могу вызвать метод вне анонимного класса с тем же именем - PullRequest
2 голосов
/ 31 октября 2008

Код в конце выдает ошибку компиляции:

NotApplicable.java:7: run() in  cannot be applied to (int)
                run(42);
                ^
1 error

Вопрос в том, почему? Почему javac считает, что я вызываю run () и не находит run (int bar)? Это правильно называется foo (int bar). Почему я должен использовать NotApplicable.this.run (42) ;? Это ошибка?

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

Ответы [ 3 ]

16 голосов
/ 31 октября 2008

Объяснение поведения вашего примера кода состоит в том, что this определен как класс, внутри которого вы в данный момент находитесь «наиболее». В этом случае вы «большинство» внутри анонимного внутреннего класса, который может выполнять подклассы, и нет метода, который соответствует run(int). Чтобы расширить поиск, укажите, какой this вы хотите использовать, указав NotApplicable.this.run(42).

JVM будет оценивать следующим образом:

this -> выполняющийся в данный момент экземпляр Runnable с методом run()

NotApplicable.this -> выполняющийся в данный момент экземпляр NotApplicable с методом run(int)

Компилятор будет искать в дереве вложенности первый метод, соответствующий ИМЯ метода. - Спасибо DJClayworth за это разъяснение

Анонимный внутренний класс не является подклассом внешнего класса. Из-за этой взаимосвязи и внутренний класс, и внешний класс должны иметь метод с точно такой же сигнатурой, а самый внутренний блок кода должен иметь возможность определить, какой метод он хочет запустить.

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" );
    }
}
1 голос
/ 31 октября 2008

Насколько я помню, правила выбора метода для запуска между вложенными классами примерно такие же, как правила выбора метода в дереве наследования. Это означает, что то, что мы получаем, не перегружено, оно скрыто. Разница между ними имеет решающее значение для понимания методов наследования.

Если ваш Runnable был объявлен как подкласс, то метод run () скрыл бы метод run (int) в родительском объекте. Любой вызов run (...) попытался бы выполнить вызов в Runnable, но потерпел бы неудачу, если он не мог соответствовать сигнатурам. Поскольку foo не объявлен в дочернем элементе, вызывается тот, что указан в родительском элементе.

Тот же принцип происходит здесь. Посмотрите ссылки на «метод сокрытия», и это должно быть ясно.

0 голосов
/ 31 октября 2008

Это потому, что run повторно объявляется при вводе области действия new Runnable() {}. Все предыдущие привязки к запуску становятся недоступными. Это как если бы вы делали это:

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

Компилятор не будет искать что-либо, соответствующее типу x, до конца стека областей, он просто остановится, когда найдет первые ссылки и обнаружит, что типы несовместимы.

ПРИМЕЧАНИЕ: Это не так, как если бы не смог сделать это ... просто для того, чтобы сохранить собственное здравомыслие, было решено, что не следует. 1013 *

...