在 Kotlin 中,".()" 是什么意思?

68
我看到过一些函数的参数是通过 ClassName.() 给出的。 这似乎不是扩展函数,扩展函数应该是 ClassName.Function()。
一个例子是 Kotterknife: Kotterknife
private val View.viewFinder: View.(Int) -> View?
    get() = { findViewById(it) }

我并不太清楚它的功能,

以及MaterialDrawerKt

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

当代码允许你直接调用时

drawer {
    ...
}

而不是将其包在括号中作为参数传递。

这方面有没有任何文档可以参考?


3
你应该查看带有接收者的Lambda表达式: https://kotlinlang.org/docs/reference/lambdas.html#function-literals-with-receiver - Lukas Lechner
1
如果一个函数的最后一个参数是lambda表达式,你可以将它从括号中提取出来,放到由{ }包围的块中,就像你的drawer示例一样。 - Lukas Lechner
6个回答

64

在 Kotlin 中,一个不接受任何参数并且不返回任何内容的函数如下所示:

var function : () -> Unit
你的代码中的函数不需要输入任何参数,也不会返回任何东西,但是它会在一个对象上被调用,这与普通函数不同。
例如,
class Builder (val multiplier: Int) {
    
    fun invokeStuff(action: (Builder.() -> Unit)) {
        this.action()
    }
    
    fun multiply(value: Int) : Int {
        return value * multiplier
    }
}

这里重要的部分是我们声明了action的类型。

action: (Builder.() -> Unit)

这是一个不返回任何东西的函数,不需要输入参数,但在类型为Builder的对象上调用

这意味着当我们像以下这样使用该构建器时:

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

已将this的上下文设置为生成器对象,我们可以调用在生成器内声明的函数。

点击此处了解更多


31
这是一个好问题。所以当你有这种语句时:T.(),它意味着在你传入的lambda函数中,“this”(即当前对象)将是类型T。让我们看看它有多容易理解:
假设我们有一个类,其中有一个名为myFun的函数,该函数采用以下定义的lambda函数:
 class MyObject {
        fun myFun(doSomething: MyObject.()->Unit) {
            doSomething()
        }

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

要调用这个函数,我会这样做:

MyObject().myFun { doAnotherThing() }

看到它如何使用MyObject()引用作为"this"。这实际上是调用了this.doAnotherThing(),其中this是刚创建的Myobject()实例。

也可以这样做:

MyObject().apply{myFun { doAnotherThing() }}  

嗨,为什么你每次都使用 doAnotherThing() 进行调用?doAnotherThing() 在这里与什么相关?我不理解。 - jpganz18
1
我只是在演示中表明,引用不需要使用 this.doAnotherThing(),只需使用 doAnotherThing()。 - j2emanue
这个答案实际上更有帮助。 - Inoy
但是为什么你仍然可以在myFun块中调用基本上任何函数呢?例如MyObject().myFun { functionNotDeclaredInMyObject() } - nsko

8

有一个误解认为 T.() -> Y 是 (T.()) -> Y,但实际上是 T.(()->Y)。 我们知道 (X)->Y 是一个 lambda 表达式,所以 T.(X)->Y 是对 T 的扩展。

如果没有参数,形式为 T.() -> Y

有趣的是,我们可以通过以下两种方式来调用它。

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
这是一个非常有趣的发现。我非常好奇另一个方法(me)是如何工作的。 - Panda World

5

@Kris Roofe的答案已经让事情变得清晰明了。让我再补充一些细节。

fun Activity.drawer 表示我们在Activity类中创建了一个名为drawer的扩展函数。这就是我们可以直接从Activity类或其子类中调用drawer方法的原因。

更多关于扩展函数的信息请点击这里

(setup: DrawerBuilderKt.() -> Unit = {}) 在这个语句中,我们可以看到Kotlin高阶函数的强大之处。以下是高阶函数的简单介绍:它是一个接受函数作为参数或返回函数的函数。 因此,这里的setup参数是一个返回Nothing或Unit(与Java中的Void相同)的函数。DrawerBuilderKt.()表示该函数可以使用DrawerBuilderKt类的对象来调用。= {}表示setup参数是可选的。因此,该函数不带参数且不返回任何内容。

更多关于高阶函数的信息请点击这里这里。更多关于可选参数的信息请点击这里

private val View.viewFinder: View.(Int) -> View?它将一个函数存储在一个属性中。这里有更多相关信息。其余部分与上面解释的相同。

希望这可以帮到您。


0
这个有文档可以参考。这个概念被称为“带接收者的函数字面值”,在这里有详细描述:https://kotlinlang.org/docs/lambdas.html#function-literals-with-receiver
简而言之,lambda表达式可以访问接收对象的成员,并且可以使用this引用该对象。正如之前提到的,它基本上是一个作为扩展函数的lambda表达式。

0
"T.()" 这是一个将函数作为参数的扩展函数。
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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接