66

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?

Pravin Divraniya
  • 4,223
  • 2
  • 32
  • 49
Allan W
  • 2,791
  • 4
  • 23
  • 41
  • 3
    You should take a look on lambdas with receivers: https://kotlinlang.org/docs/reference/lambdas.html#function-literals-with-receiver – Lukas Lechner Jun 08 '17 at 06: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 – Lukas Lechner Jun 08 '17 at 06:02

5 Answers5

63

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.

bric3
  • 40,072
  • 9
  • 91
  • 111
LF00
  • 27,015
  • 29
  • 156
  • 295
28

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() }}  
j2emanue
  • 60,549
  • 65
  • 286
  • 456
  • Hi, why you call it using doAnotherThing() all the times? how is doAnotherThing() related here? I dont understand it – jpganz18 Oct 08 '20 at 12:33
  • 1
    im just demonstrating that the reference does not need to use this.doAnotherThing() simply doAnotherThing() – j2emanue Oct 16 '20 at 02:30
  • This answer is actually more helpful – Inoy Sep 28 '22 at 18:25
  • But why can you still call basically any function in myFun block? E.g MyObject().myFun { functionNotDeclaredInMyObject() } – nsko Apr 09 '23 at 13:35
7

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)
}
BollMose
  • 3,002
  • 4
  • 32
  • 41
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.

Pravin Divraniya
  • 4,223
  • 2
  • 32
  • 49
0

"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
}
gandalivs
  • 383
  • 3
  • 4