Converting Thread/Runnable implementation from Java to Kotlin

Craig Otis picture Craig Otis · Apr 12, 2017 · Viewed 9.2k times · Source

I have an existing Java class ThreadUtils with a method every that looks like:

public class ThreadUtil {

    public static Thread every(int seconds, Runnable r) {
        Thread t = new Thread(() -> {
            while(true) {
                r.run();
                try {
                    Thread.sleep(1000 * seconds);
                } catch (InterruptedException e) {
                    return;
                }
            }
        });
        t.start();
        return t;
    }
}

And I'm trying to convert it to Kotlin. I'm a bit hung up on the Runnable closure. This fails with a bad return:

fun every(seconds: Int, r: Runnable): Thread {
    val t = Thread({
        while (true) {
            r.run()
            try {
                Thread.sleep((1000 * seconds).toLong())
            } catch (e: InterruptedException) {
                return // ERROR: This function must return a value of type Thread
            }
        }
    })
    t.start()
    return t
}

I also tried pulling the Runnable out just to help myself separate things, but this also fails the same way:

fun every(seconds: Int, r: Runnable): Thread {
    val internalRunnable = Runnable {
        while (true) {
            r.run()
            try {
                Thread.sleep((1000 * seconds).toLong())
            } catch (e: InterruptedException) {
                return // ERROR: This function must return a value of type Thread
            }
        }
    }
    val t = Thread(internalRunnable)
    t.start()
    return t
}

How can I implement a @FunctionalInterface or similar-style closure/lambda that doesn't try to return from the function in which it's being defined?

Answer

hotkey picture hotkey · Apr 12, 2017

In Kotlin, return statements inside lambdas work differently from those in Java. If you write just return, it means return from the innermost function declared with keyword fun, and it ignores lambdas -- in your code, it means 'return from every'.

To return from a lambda, use qualified return@label-- in your case, it's return@Thread (and return@Runnable for the second example), like in this simplified snippet:

for (i in 1..4) {
    Thread { 
        if (i % 2 == 0)
            return@Thread
        println("Thread $i")
    }.run()
}

(runnable demo of this code)

Also, there is a thread { ... } function in kotlin-stdlib that you might find useful (and, similarly, the return statement for its lambda is return@thread).

You can find a more detailed explanation in the language reference and in this answer.