kotlinreceiver

In Kotlin, how can I bind an extension method to work within a receiver scope function


This feels like some kind of inconsistency because it works with a standalone function, but not with a class/interface method:

class TestClass {
    val foo = "bar"
}

fun testScope(scope: TestClass.() -> Any) {

    val test = TestClass()

    scope(test)

}


// this works
fun TestClass.standaloneRunInTest() {
    println(foo)
}

interface Example {
    
    val name: String

    // but this doesn't?
    fun TestClass.interfaceRunInTest() {
        println(foo + name)
    }

    object Instance: Example {
        override val name: String = "Baz"
    }

}


fun main() {

    testScope {
        standaloneRunInTest() // prints "bar"
        Example.Instance.interfaceRunInTest() // expected to print "barbaz", but gets Unresolved reference: interfaceRunInTest
    }

}

More likely though I am doing something silly!


Solution

  • Example.Instance is not an instance of TestClass. You're using syntax as if you're trying to call interfaceRunInTest() as a function of Example.Instance, which won't work.

    To call an extension function that is defined in the scope of a type, you have to bring both types into scope as receivers, for example:

    testScope {
        Example.Instance.run { interfaceRunInTest() }
    }
    
    // or 
    Example.Instance.run {
        testScope {
            interfaceRunInTest()
        }
    }
    

    The run and with scope functions are useful for this purpose.