Подтвердить что ты не робот

Как использовать TimerTask с lambdas?

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

Пример можно увидеть здесь: Java 7 vs Java 8:

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

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

Runnable runnable = () -> checkDirectory();

или

Runnable runnable = this::checkDirectory;

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

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

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

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

Итак, мой вопрос : есть ли способ использовать lambdas при построении TimerTask?

Мне нужно следующее:

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

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

4b9b3361

Ответ 1

Отмечая сначала, что Timer является фактически устаревшим API, но тем не менее занимая ваш вопрос, вы можете написать небольшую обертку вокруг него, которая адаптировала бы метод schedule для принятия Runnable, а внутри - d превратите 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;
  }
}

Ответ 2

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

schedule(() -> {
    System.out.println("Task #1 is running");
}, 500);

Ответ 3

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

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.

Ответ 4

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

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