26

As you hopefully know you can use lambdas in Java 8, for example to replace anonymous methods.

An example can be seen here of Java 7 vs Java 8:

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

Can be expressed as both the following ways in Java 8:

Runnable runnable = () -> checkDirectory();

or

Runnable runnable = this::checkDirectory;

This is because Runnable is a functional interface, having only one (abstract) public non-default method.

However... For TimerTask we have the following:

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

Looks familiar, right?
Using a lambda expression does not work though, because TimerTask is an abstract class, even though it has only one abstract public non-default method, it is not an interface and hence no functional interface either.
It is also not refactored into an interface with default implementations, because it carries state, so that cannot be done then.

So my question: Is there any way to use lambdas when constructing the TimerTask?

What I wanted is the following:

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

Instead of some ugly anonymous inner class, is there any way to make it nicer?

skiwi
  • 66,971
  • 31
  • 131
  • 216
  • 10
    Since you use modern features, why not go all the way and use a `ScheduledExecutorService` instead of a `TimerTask`? ;) – fge Mar 13 '14 at 12:21
  • 2
    @fge Well, I didn't know it existed, until now... Now I think about it, does Java not have their APIs mention that there is a newer similar feature available, when the old feature is not yet *deprecated*? – skiwi Mar 13 '14 at 12:22
  • In case of `Timer`, Josh Bloch's Effective Java 2nd Edition advises that. It is not an official position of the JDK API, though. – Marko Topolnik Mar 13 '14 at 12:23
  • 1
    Eh, no... This is admittedly a great lack in their documentation. Similarly, they don't mention `Files` in the `File` doc; – fge Mar 13 '14 at 12:23

5 Answers5

14

Noting first that Timer is effectively an antiquated API, but entertaining your question nevertheless, you could write a small wrapper around it which would adapt the schedule method to accept a Runnable, and on the inside you'd turn that Runnable into a TimerTask. Then you would have your schedule method which would accept a lambda.

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;
  }
}
Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
1

To complete Marko Topolnik's answer about Timer, you just have to call schedule method with a lambda.

schedule(() -> {
    System.out.println("Task #1 is running");
}, 500);
François SAMIN
  • 360
  • 2
  • 7
  • `Runnable` is an interface, you can implement it with a lambda. So you can call the `schedule` method with a lambda. Here is a working gist https://gist.github.com/fsamin/ec5aa79bc23965eca277 – François SAMIN Dec 09 '15 at 19:40
  • 1
    Misunderstood answer; you're entirely correct of course. – gerardw Dec 10 '15 at 15:38
1

While Marko's answer is perfectly correct, I prefer my implementation:

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

This gives you more control over the timer, and you have a static utility class. Idealy give it a name that won't conflict with other common thread class, so not Task, Job, Timer.

Kalec
  • 2,681
  • 9
  • 30
  • 49
1

I know this is an old post, but for completeness, I wanted to include the wrapper solution posted by Flown:

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

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

then your call can become:

timer.schedule(wrap(this::checkDirectory), delay);
Gary
  • 4,426
  • 1
  • 22
  • 19
-1

You can also create a Timer easily with lambdas from the Swing API if you want (but not with TimerTask) :

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

lapsus63
  • 99
  • 1
  • 3