添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

In Android, I often see this as the recommended way to periodically call a function:

Handler handler = new Handler();
private Runnable runnableCode = new Runnable() {
    @Override
    public void run() {
      // Do something here
      // Repeat this the same runnable code block again another 2 seconds
      handler.postDelayed(runnableCode, 2000);
handler.post(runnableCode);

I tried to convert this to Kotlin:

val handler = Handler()
val runnableCode = object : Runnable {
    override fun run() {
        // do something
        handler.postDelayed(runnableCode, 2000)
handler.post(runnableCode)

This fails to compile with "Variable ‘runnableCode’ must be initialized. I don’t really understand this limitation, as the value is initialized. The only workaround I could think of was using handler.postDelayed(this, 2000), but this doesn’t seem to work for SAMs:

val handler = Handler()
val runnableCode = Runnable {
    // do something
    handler.postDelayed(this, 2000) // this does not exist
handler.post(runnableCode)

this does not exist within SAM function literals (if there’s a labeled this my IDE can’t find it), and I can’t find a way to access the Runnable instance itself. Is there a known workaround for this, or is it an intentional language limitation? I haven’t been able to find any documentation on the subject.

A simple try.kotlinlang.org code to demonstrate the issue:

fun main(args: Array<String>) {
    runnable.run()
    runnable2.run()
val runnable: Runnable = Runnable {    
    //println(this)  // does not compile
val runnable2: Runnable = object : Runnable {
    override fun run() {
        println(this)
              

I don’t know the original consideration behind this, but it makes sense to me: If you want to use the ‘SAM function literal’, which is more a functional programming style, ‘this’ pointer is kind of confusing.

So by forcing you to use the ‘object : Class’ anonymous class syntax, you will know it’s more a traditional class, where ‘this’ makes more sense.

this in a lambda refers to the instance of the containing class, if any. A lambda is conceptually a function, not a class, so there is no such thing as a lambda instance to which this could refer.

The fact that a lambda can be converted into an instance of a SAM interface does not change this. Having this in a lambda mean different things depending on whether the lambda gets SAM-converted would be extremely confusing.

I understand that a SAM lambda doesn’t really fit any of the rules documented at https://kotlinlang.org/docs/reference/this-expressions.html and would required a special case. I was just surprised that there was no reference to the instance being created from within itself.

It is also impossible to make them recursive or queue themselves with a Handler.

By the way, while “this” does not work in a SAM, it does work in your original code:

val handler = Handler()
val runnableCode = object : Runnable {
    override fun run() {
        // do something
        handler.postDelayed(this, 2000)
handler.post(runnableCode)
              

Here’s what I did to work around the same problem:

fun runnable(body: Runnable.(Runnable)->Unit) = object: Runnable {
    override fun run() {
        this.body(this)

Then you just change Runnable to runnable and everything works as you expect (and you can use this):

val handler = Handler()
val runnableCode = runnable {
    // do something
    handler.postDelayed(this, 2000) // perfectly OK now
handler.post(runnableCode)
              

Since it’s an extension function you don’t need to pass this as a parameter. I normally use this:

inline fun runnable(crossinline body: Runnable.() -> Unit) = object : Runnable {
    override fun run() = this.body()
              

But why using lambdas when you need a reference to the code block itself ? That’s what functions are about : naming a set of instructions so you can easily refer to it.
In my point of view, using lambdas for such intricated cases is harmful. You don’t properly separate concerns, and prevent proper signature/documentation.

In Java FX, there is a scene: void addListener(ChangeListener<? super T> listener); by SAM.
If I can get the ref of ChangeListener that I can use removeListener(Listener).
Maybe it is more …pretty?