Как использовать TimerTask с лямбдами?

Как вы, надеюсь, знаете, в Java 8 можно использовать лямбда-выражения, например, для замены анонимных методов.

Здесь можно увидеть пример Java 7 и Java 8:

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        checkDirectory();
    }
};

В Java 8 можно выразить двумя способами:

Runnable runnable = () -> checkDirectory();

or

Runnable runnable = this::checkDirectory;

Это связано с тем, что Runnable является функциональным интерфейсом, имеющим только один (абстрактный) общедоступный метод не по умолчанию.

Однако... Для TimerTask имеем следующее:

TimerTask timerTask = new TimerTask() {
    @Override
    public void run() {
        checkDirectory();
    }
};

Выглядит знакомо, правда?
Однако использование лямбда-выражения не работает, поскольку TimerTask является абстрактным классом, хотя у него есть только один абстрактный общедоступный метод не по умолчанию, он не является интерфейсом и, следовательно, не является функциональным интерфейсом.< br> Он также не рефакторинг в интерфейс с реализациями по умолчанию, потому что он несет состояние, так что тогда это невозможно сделать.

Итак, мой вопрос: есть ли способ использовать лямбда-выражения при построении TimerTask?

Я хотел следующее:

Timer timer = new Timer();
timer.schedule(this::checkDirectory, 0, 1 * 1000);

Вместо какого-то уродливого анонимного внутреннего класса, есть ли способ сделать его лучше?


person skiwi    schedule 13.03.2014    source источник
comment
Поскольку вы используете современные функции, почему бы не пойти до конца и не использовать ScheduledExecutorService вместо TimerTask? ;)   -  person fge    schedule 13.03.2014
comment
@fge Ну, я не знал, что он существует, до сих пор ... Теперь я думаю об этом, разве в Java не упоминается их API, что есть более новая аналогичная функция, когда старая функция еще не устарела. ?   -  person skiwi    schedule 13.03.2014
comment
В случае Timer это рекомендуется в книге Джоша Блоха «Эффективная Java 2nd Edition». Однако это не официальная позиция JDK API.   -  person Marko Topolnik    schedule 13.03.2014
comment
Э, нет... Это, по общему признанию, большой недостаток в их документации. Точно так же они не упоминают Files в документе File;   -  person fge    schedule 13.03.2014


Ответы (5)


Отметив сначала, что Timer фактически является устаревшим API, но, тем не менее, развлекая свой вопрос, вы можете написать небольшую оболочку вокруг него, которая адаптирует метод schedule для приема Runnable, а внутри вы превратите это Runnable в TimerTask. Тогда у вас будет метод schedule, который будет принимать лямбду.

public class MyTimer {
  private final Timer t = new Timer();

  public TimerTask schedule(final Runnable r, long delay) {
     final TimerTask task = new TimerTask() { public void run() { r.run(); }};
     t.schedule(task, delay);
     return task;
  }
}
person Marko Topolnik    schedule 13.03.2014
comment
Технически ваш ответ правильный, однако лично я выберу ScheduledExecutorService вместо TimerTask. - person skiwi; 13.03.2014
comment
какой текущий API тогда? - person krivar; 30.10.2015

Чтобы завершить ответ Марко Топольника о Timer, вам просто нужно вызвать метод schedule с лямбдой.

schedule(() -> {
    System.out.println("Task #1 is running");
}, 500);
person François SAMIN    schedule 27.02.2015
comment
Runnable — это интерфейс, вы можете реализовать его с помощью лямбды. Таким образом, вы можете вызвать метод schedule с лямбдой. Вот рабочая суть gist.github.com/fsamin/ec5aa79bc23965eca277. - person François SAMIN; 09.12.2015
comment
Непонятый ответ; вы совершенно правы конечно. - person gerardw; 10.12.2015

Хотя ответ Марко совершенно правильный, я предпочитаю свою реализацию:

public class FunctionalTimerTask extends TimerTask {

    Runnable task;

    public FunctionalTimerTask(Runnable task) {
        this.task = task;
    }

    @Override
    public void run() {
        task.run();
    }
}

 public static class Task {
    public static TimerTask set(Runnable run) {
        return new FunctionalTimerTask(() -> System.err.println("task"));
    }
}

 Timer timer = new Timer(false);
 timer.schedule(Task.set(() -> doStuff()), TimeUnit.SECONDS.toMillis(1));

Это дает вам больше контроля над таймером, и у вас есть статический служебный класс. В идеале дайте ему имя, которое не будет конфликтовать с другим общим классом потока, а не с Task, Job, Timer.

person Kalec    schedule 03.07.2019

Я знаю, что это старый пост, но для полноты картины я хотел включить решение-оболочку, опубликованное Полетел:

private static TimerTask wrap(Runnable r) {
  return new TimerTask() {

    @Override
    public void run() {
      r.run();
    }
  };
}

тогда ваш вызов может стать:

timer.schedule(wrap(this::checkDirectory), delay);
person Gary    schedule 24.10.2020

Вы также можете легко создать таймер с помощью лямбда-выражений из Swing API, если хотите (но не с TimerTask):

new javax.swing.Timer(1000, (ae) -> this::checkDirectory).start();

person lapsus63    schedule 21.12.2016