Can I "return early" inside a Runnable?

Inside a Runnable block, I want to do some "guard block", like so:

var condition: String? = null
Runnable {
    if (condition == null) return
    // do something
}

but compiler says "return is not allowed here"??

2 answers

  • answered 2018-11-08 07:39 s1m0nw1

    There are two ways you can make this work:

    As mentioned in the comments, you can use a "qualified return" as discussed here. That's also what IntelliJ (AndroidStudio as well I guess) suggests: enter image description here

    As an alternative, you can define your Runnable as an anonymous class which enables you to use ordinary return statements:

     object: Runnable {
         override fun run() {
             if (condition == null) return
             // do something
         }
     }
    

    IntelliJ will now suggest to transform this object to a lambda which will result in the exact same thing with a qualified return:

    Runnable {
        if (condition == null) return@Runnable
        // do something
    }
    

    PS: No need to feel dumb! I suppose you would have found it easily with the right wording. It's good to know what Runnable {} is here. It's basically a lambda which is based on SAM conversion (works with Java types with a single abstract method)

  • answered 2018-11-08 10:04 Zoe

    To expand on s1m0nw1's answer, using return@Runnable is correct.

    @Runnable is, in this case, defining what you return. It also works with loops, but also methods. Here's an example with loops:

    fun test(){
        one@for(i in 0..100){
            two@for(j in 0..i){
                if(j == 20){
                    break;//this breaks two
                }
            }
        }
    }
    

    This example breaks the second when j == 20. If you want to break the outer loop when j is 20 (this might not be the best example, but you still get the general idea), you'd use break@one. For loops, this only works if you explicitly declare a label.

    You basically have to specify what you want to break/return/continue (depending on what you use) in some cases. Returning from a regular method, or breaking a single loop doesn't need explicit labeling, but if you use lambda, or want to break outer loops, you'd target those using [return/break/continue]@LabelMethodClassOrInterfaceName.

    In the example I added, if you want to return instead of break, you could use return@test. However, it's slightly pointless since return in that context implies from the method.

    With this type of lambda, you're forced to use return@InterfaceName. InterfaceName is replaced with whatever interface you're using (in this case Runnable).

    Or you could of course get IntelliJ to auto-complete it for you.