Runnable :: новый против нового Runnable () - PullRequest
0 голосов
/ 07 января 2019

Почему не работает первый из следующих примеров?

  • run(R::new); метод R.run не вызывается.
  • run(new R()); метод R.run называется .

Оба примера скомпилированы.

public class ConstructorRefVsNew {

  public static void main(String[] args) {
      new ConstructorRefVsNew().run(R::new);
      System.out.println("-----------------------");
      new ConstructorRefVsNew().run(new R());
  }

  void run(Runnable r) {
      r.run();
  }

  static class R implements Runnable {

      R() {
          System.out.println("R constructor runs");
      }

      @Override
      public void run() {
          System.out.println("R.run runs");
      }
  }
}

Вывод:

  R constructor runs
  -----------------------
  R constructor runs
  R.run runs

В первом примере вызывается конструктор R, он возвращает лямбду (которая не является объектом):

Но тогда как это возможно, чтобы пример был успешно скомпилирован?

Ответы [ 4 ]

0 голосов
/ 07 января 2019

Сравните два звонка:

((Runnable)() -> new R()).run();
new R().run();

По ((Runnable)() -> new R()) или ((Runnable) R::new) вы создаете новый Runnable, который ничего не делает 1 .

К new R() вы создаете экземпляр класса R , где метод run четко определен.


1 На самом деле он создает объект R, который не влияет на выполнение.


Я думал о том, чтобы идентично обрабатывать 2 вызова без изменения метода main. Нам нужно перегрузить run(Runnable) с run(Supplier<Runnable>).

class ConstructorRefVsNew {

    public static void main(String[] args) {
        new ConstructorRefVsNew().run(R::new);
        System.out.println("-----------------------");
        new ConstructorRefVsNew().run(new R());
    }

    void run(Runnable r) {
        r.run();
    }

    void run(Supplier<Runnable> s) {
        run(s.get());
    }

    static class R implements Runnable { ... }
}
0 голосов
/ 07 января 2019

Первый пример:

new ConstructorRefVsNew().run(R::new);

более или менее эквивалентно:

new ConstructorRefVsNew().run( () -> {new R();} );

В результате вы просто создаете экземпляр R, но не вызываете его run метод.

0 голосов
/ 07 января 2019

Метод run ожидает Runnable.

Легкий случай - new R(). В этом случае вы знаете, что результатом является объект типа R. R сам по себе является работоспособным, у него есть метод run, и вот как это видит Java.

Но когда вы передаете R::new, происходит что-то еще. Вы говорите, что создаете анонимный объект, совместимый с Runnable, чей метод run выполняет операцию, которую вы передали ему.

Операция, которую вы передали, не является R run методом. Операция является конструктором R. Таким образом, вы передали ему анонимный класс, например:

new Runnable() {

     public void run() {
         new R();
     }
}

(Не все детали одинаковы, но это наиболее близкая "классическая" конструкция Java).

R::new, когда вызывается, звонит new R(). Ни больше, ни меньше.

0 голосов
/ 07 января 2019

Ваш метод run принимает экземпляр Runnable, и это объясняет, почему run(new R()) работает с реализацией R.

R::new не эквивалентно new R(). Он может соответствовать сигнатуре Supplier<Runnable> (или аналогичным функциональным интерфейсам), но R::new нельзя использовать как Runnable, реализованный с вашим классом R.

Версия вашего run метода, который может принимать R::new, может выглядеть так (но это будет излишне сложно):

void run(Supplier<Runnable> r) {
    r.get().run();
}

Почему он компилируется?

Поскольку компилятор может сделать Runnable из вызова конструктора, и это будет эквивалентно этой версии лямбда-выражения:

new ConstructorRefVsNew().run(() -> {
    new R(); //discarded result, but this is the run() body
});

То же относится и к этим утверждениям:

Runnable runnable = () -> new R();
new ConstructorRefVsNew().run(runnable);
Runnable runnable2 = R::new;
new ConstructorRefVsNew().run(runnable2);

Но, как вы можете заметить, Runnable, созданный с помощью R::new, просто вызывает new R() в своем теле run метода.


Допустимое использование ссылки на метод для выполнения R#run может использовать экземпляр, подобный этому (но вы наверняка предпочтете использовать экземпляр r напрямую, в этом случае):

R r = new R();
new ConstructorRefVsNew().run(r::run);
...