带有 T.() 的 Kotlin 函数签名是什么意思?

What does a Kotlin function signature with T.() mean?

这是一个标准的 Kotlin 函数(据我所知)

inline fun<T> with(t: T, body: T.() -> Unit) { t.body() }

但是有人能用简单的英语写下签名的确切含义吗?它是 T 的通用函数,第一个参数为 "t" 类型 T 和第二个 "body" 函数类型,接受函数 ????并且什么都不返回(单位)

我看到这个符号 Something.() -> Something 被频繁使用,例如 Anko:

inline fun Activity.coordinatorLayout(init: CoordinatorLayout.() -> Unit) = ankoView({ CoordinatorLayout(it) },init)

但我认为在任何地方都没有解释 .() 的含义...

T.() -> Unit是一个带有接收者的扩展函数类型。

除了普通函数,Kotlin 还支持扩展函数。这种功能与普通功能的不同之处在于它具有接收器类型规范。这是通用的 T. 部分。

扩展函数中的this关键字对应的是接收者对象(在点之前传递的对象),所以可以直接调用它的方法(指父作用域中的this还是可以的 with qualifiers).


函数 with 是标准函数,是的。当前代码:

/**
 * Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
 *
 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#with).
 */
public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()

所以它是 TR 的通用函数,第一个参数 "receiver" 类型为 T,第二个参数 f 为扩展函数类型,扩展 T,返回类型 R,后者又由 with.

返回

比如你可以这样使用:

val threadInfoString = with (Thread.currentThread()) {
    // isDaemon() & getPriority() are called with new property syntax for getters & setters
    "${getName()}[isDaemon=$isDaemon,priority=$priority]"
}


请在此处查看扩展功能的文档:
kotlinlang.org/docs/reference/lambdas.html#extension-function-expressions
kotlinlang.org/docs/reference/scope-functions.html#with kotlinlang.org/docs/reference/extensions.html


已添加:

So the only valid f would be any 0-argument function defined for T?

不是真的。在 Kotlin function types and extension function types are unified 中,它们可以互换使用。例如,我们可以在需要函数 (String) -> Int 的地方传递 String::length。

// map() expects `(String) -> Int`
// argument has type `String.() -> Int`
strings.map(String::length)

Thread.() -> String & (Thread) -> String这样的类型从后台看是一样的——receiver,实际上是第一个参数。

因此以下任何函数都适用于 Thread.() -> String 参数:

fun main(args: Array<String>) {
    val f1 = fun Thread.(): String = name
    val f2 = fun Thread.() = name
    val f3: Thread.() -> String = { name }
    val f4: (Thread) -> String = { it.name }
    val f5 = { t: Thread -> t.name }
    val f6: (Thread) -> String = Thread::getNameZ
    val f7: Thread.() -> String = Thread::getNameZ
    val f8 = Thread::getNameZ
}

fun Thread.getNameZ() = name

或者您可以简单地使用 function literal ({}),就像在 threadInfoString 的示例中一样,但它仅在可以从上下文中推断出接收者类型时才有效。

这是另一个惊人的概念,名为函数文字与接收器,它看起来与扩展函数相似,不同之处在于它有.

inline fun doSomethingWithInt(receiver: Int.() -> Unit) {

    5.receiver() //Possible as receiver is Int type

    receiver(5) //possible as receiver should be passed as first Argument
}

让我们把它拆开

(receiver: Int.() -> Unit)

这与 () -> Unit 等常规 lambda 有何不同之处在于它具有类型为 Int

的接收器

与此接收器可以在 Int 类型

上调用的扩展函数非常相似

5.receiver()

并且根据 Documentation Here

A value of a function type can be invoked by using its invoke(...) operator:

f.invoke(x) or just f(x).

If the value has a receiver type, the receiver object should be passed as the first argument. Another way to invoke a value of a function type with receiver is to prepend it with the receiver object, as if the value were an extension function: 1.foo(2)

所以你也可以这样写receiver(5)

正因如此,现在我们可以编写 DSL 风格的代码。让我们看看kotlin标准库函数Apply

public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }

正如你所看到的,apply 是 T 的一个扩展函数,block 将与接收器类型 T,

因为 T.() T 将作为 lambda 中的第一个参数可用。

现在block在函数体内由block()调用,但你也可以写成this.block()或block(this)

并且在调用此 lambda 之后 我们将 return T 的实例(调用 apply 的同一实例)

因此,我们在 apply 中所做的是将 lambda 作为参数,在调用它的同一个实例 T 上执行它,return 同一个 T 实例

例子

此函数的调用如下所示:

(StringBuilder("Hello ").apply({ append("Kotliner") }))

但是在 kotlin 中,如果 lambda 是最后一个参数,那么您可以简单地写 funName{}.

而不是像 funName({}) 这样的写法

所以上面的代码也可以写成

StringBuilder("Hello ").apply {
        append("Kotliner")
        append("! ")
    }

这样看起来更简洁明了。所以这样 Kotlin 提供了像 Groovy.

这样的 DSL 结构

这是关于同一主题的very nice blog

叫做"Function literals with receiver"

函数类型可以选择有一个额外的接收者类型,它在符号中的点之前指定:类型 A.(B) -> C 表示可以在 A 的接收者对象上调用的带有参数的函数B 和 return C 的值。具有接收者的函数文字通常与这些类型一起使用。

你可以在kotlin官方文档中找到答案:Function literals with receiver