What does ".()" mean in Kotlin?
Asked Answered
X

6

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?

Xhosa answered 8/6, 2017 at 5:35 Comment(2)
You should take a look on lambdas with receivers: kotlinlang.org/docs/reference/…Syrup
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 exampleSyrup
M
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.

Miscellanea answered 8/6, 2017 at 6:6 Comment(0)
D
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() }}  
Dissuasion answered 23/4, 2019 at 7:5 Comment(4)
Hi, why you call it using doAnotherThing() all the times? how is doAnotherThing() related here? I dont understand itLifework
im just demonstrating that the reference does not need to use this.doAnotherThing() simply doAnotherThing()Dissuasion
This answer is actually more helpfulConflation
But why can you still call basically any function in myFun block? E.g MyObject().myFun { functionNotDeclaredInMyObject() }Parmer
M
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)
}
Munro answered 19/4, 2020 at 12:53 Comment(1)
That's really interesting finding. I'm quite curious how anotherMehtod(me) works.Iconolatry
B
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.

Besprinkle answered 24/7, 2018 at 8:15 Comment(0)
H
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
}
Hagfish answered 4/8, 2023 at 8:29 Comment(0)
B
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.

Bedraggled answered 25/9, 2023 at 5:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.