74

I've seen examples where a function has an argument given by ClassName.() This doesn't seem to be an extension function, which is ClassName.Function()

An example is Kotterknife:

private val View.viewFinder: View.(Int) -> View?
    get() = { findViewById(it) }

Which I don't quite know the function of,

and MaterialDrawerKt

fun Activity.drawer(setup: DrawerBuilderKt.() -> Unit = {}): Drawer {
    val builder = DrawerBuilderKt(this)
    builder.setup()
    return builder.build()
}

Where the code allows you to directly call

drawer {
    ...
}

rather than give it arguments surrounded by the parentheses.

Is there any documentation on this anywhere?

2
  • 3
    You should take a look on lambdas with receivers: kotlinlang.org/docs/reference/… Commented Jun 8, 2017 at 6:00
  • 1
    And if the last parameter of a function is a lambda, you can pull it out of the parenthesis and into a block surrounded by { }, as in your drawer example Commented Jun 8, 2017 at 6:02

6 Answers 6

66

A function that takes in nothing and returns nothing in Kotlin looks like:

var function : () -> Unit

The difference is that the function in your code takes in nothing, returns nothing, but is invoked on an object.

For example,

class Builder (val multiplier: Int) {
    
    fun invokeStuff(action: (Builder.() -> Unit)) {
        this.action()
    }
    
    fun multiply(value: Int) : Int {
        return value * multiplier
    }
}

The important bit here is the way we've declared the type of action

action: (Builder.() -> Unit)

This is a function that returns nothing, takes in nothing but is invoked on an object of type Builder.

This means when we use this builder like so

var builder = Builder(10)
builder.invokeStuff({
    var result = multiply(1)
    println(result)
})

The context of this has been set to the builder object and we can invoke functions declared within the builder.

Refer more here.

32

this is a good question. so when you have this kind of statement: T.()

it means that in the lamda you will be passing in, "this" (which is the current object) will be of type T. Lets take a look how easy it its to understand:

Lets say we have some class with a function called myFun which takes in a lambda defined like this:

 class MyObject {
        fun myFun(doSomething: MyObject.()->Unit) {
            doSomething()
        }

        fun doAnotherThing() {
            Timber.d("myapp", "doing another thing")
        }
    }

to call this function i'd do this:

MyObject().myFun { doAnotherThing() }

see how it knew to use the MyObject() reference as the "this". this is really calling this.doAnotherThing() where this is the Myobject() instance just created.

could have also done this:

MyObject().apply{myFun { doAnotherThing() }}  
4
  • Hi, why you call it using doAnotherThing() all the times? how is doAnotherThing() related here? I dont understand it
    – jpganz18
    Commented Oct 8, 2020 at 12:33
  • 1
    im just demonstrating that the reference does not need to use this.doAnotherThing() simply doAnotherThing()
    – j2emanue
    Commented Oct 16, 2020 at 2:30
  • This answer is actually more helpful
    – Inoy
    Commented Sep 28, 2022 at 18:25
  • But why can you still call basically any function in myFun block? E.g MyObject().myFun { functionNotDeclaredInMyObject() }
    – nsko
    Commented Apr 9, 2023 at 13:35
8

There is a misunderstanding that T.() -> Y is (T.()) -> Y, but actually is T.(()->Y). As we know (X)->Y is a lambda, so T.(X)->Y is an extension on T.

If there is no parameter, the form is T.() -> Y

It's interesting that we can call it in two ways as the blew.

import kotlinx.coroutines.*


open class MyClass(var name: String){
    open fun something(){println("myclass something")}
}


fun main() = runBlocking{
    val me = MyClass("Boll")
    val someMethod: MyClass.(Int) -> String = { n ->
        List(n){"X"}.joinToString(separator="", postfix=":${this.name}")
    }
    val some = me.someMethod(10)
    //val some = someMethod(me, 10)
    println(some)

    val anotherMehtod: MyClass.() -> String = { 
        "Y:"+this.name
    }
    //val another = me.anotherMehtod()
    val another = anotherMehtod(me) 
    println(another)
}
1
  • 1
    That's really interesting finding. I'm quite curious how anotherMehtod(me) works. Commented May 12, 2020 at 10:20
5

Answer of @Kris Roofe make the things clear. Let me add more to it.

fun Activity.drawer means that we are making an extension function name drawer in Activity class. That's the reason we can call drawer method directly from an Activity class or child of an Activity class.

More on extension functions here.

(setup: DrawerBuilderKt.() -> Unit = {}) In this statement we can see the power of kotlin higher order functions. Small intro of Higher order functions :- It is a function that takes functions as parameters, or returns a function. So here setup param is a function which return Nothing or Unit(Same as Void in java). DrawerBuilderKt.() means that the function can be invoked using object of DrawerBuilderKt class. = {} means that setup parameter is optional. So the function takes no parameters and return nothing.

More on Higher order functions here and here. More on optional parameter here.

private val View.viewFinder: View.(Int) -> View? it store a function in a property. Here more info about the same. Rest of the things are same as explained above.

Hope this will help.

1

"T.()" this a extension function as parameter

class Human {
    fun eat() {
        println("only eat")
    }
}

fun Human.sleep(){
    println("krok")
}

fun whatHumanCanDo(test:Human.() -> Unit) {
    val human = Human();
    human.test()
    human.sleep()
}

fun main() {
  
  whatHumanCanDo {
      println("help")
      println("each")
      println("other")
      println("and")
      println("respect")
  }

    val badHuman = Human()
    badHuman.eat()
    badHuman.sleep()
    //badHuman.test() //not recognized
}
1

There is documentation on this. The concept is called "function literals with reciever" and is described here: https://kotlinlang.org/docs/lambdas.html#function-literals-with-receiver.

In essence, the lambda has access to the receiving object's members and can refer to the object with this. As mentioned previously, it's basically a lambda as an extension function.

Not the answer you're looking for? Browse other questions tagged or ask your own question.