Search code examples
javakotlinkotlin-interop

Converting Thread/Runnable implementation from Java to Kotlin


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?


Solution

  • 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.