Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variable 'runnable' must be initialized

Why does Kotlin complains about this:

class MyActivity : Activity {
  private var handler:Handler = Handler()

  private var runnable: Runnable = Runnable {
    /* Do something very important */
    handler.postDelayed([email protected], 5000)
  }
}

Compiler complains that Variable 'runnable' must be initialized in the Line were it's posted again by handler. This does work in plain Java:

private Handler handler = new Handler();

private Runnable runnable = new Runnable() {
    @Override
    public void run() {
        handler.postDelayed(runnable, 5000);
    }
};
like image 384
GeneralOfTheFelixLegions Avatar asked Apr 07 '16 12:04

GeneralOfTheFelixLegions


2 Answers

Kotlin considers a property uninitialized until the end of its initializer, therefore it cannot be used inside its own initializer, even in lambdas. This semantics is similar to the limitation of local variable usage inside its initializer.

There are several workarounds:

  • Use object expression which lets you reference this of the declared object:

    private var runnable: Runnable = object : Runnable {
        override fun run() {
            /* Do something very important */
            handler.postDelayed(this, 5000)
        }
    }
    

    This works well only for interfaces as a replacement for lambdas and is not very pretty altogether.

  • Use lateinit var or a delegated property with Delegates.notNull():

    private lateinit var runnable: Runnable
    init {
        runnable = Runnable { 
            /* Do something very important */
            handler.postDelayed(runnable, 5000)
        }
    }
    

    The same initializer will work with this declaration:

    private var runnable: Runnable by Delegates.notNull()
    
  • Implement and use self-reference for initializers on your own:

    class SelfReference<T>(val initializer: SelfReference<T>.() -> T) {
        val self: T by lazy {
            inner ?: throw IllegalStateException("Do not use `self` until initialized.")
        }
    
        private val inner = initializer()
    }
    
    fun <T> selfReference(initializer: SelfReference<T>.() -> T): T {
        return SelfReference(initializer).self
    }
    

    And then you can write something like

    private var runnable: Runnable = selfReference { 
        Runnable {
            /* Do something very important */
            handler.postDelayed(self, 5000)
        } 
    }
    
like image 104
hotkey Avatar answered Oct 06 '22 00:10

hotkey


You can also use

private var runnable: Runnable = Runnable {
    /* Do something very important */
    handler.postDelayed(runnable(), 5000)
}

private fun runnable() = runnable
like image 34
tango24 Avatar answered Oct 06 '22 01:10

tango24