它与扩展功能有什么关系?为什么witha function不是一个关键词?

似乎没有关于这个主题的明确文档,只有关于extensions的知识假设.

推荐答案

的确,似乎几乎没有关于接收器概念的现有文件(只有small side note related to extension functions个),这是令人惊讶的:

All these topics have documentation, but nothing goes in-depth on receivers.


第一:

What's a receiver?

Kotlin中的任何代码块都可能有一个类型(甚至多个类型)作为receiver,使接收器的功能和属性在该代码块中可用,而无需对其进行限定.

想象一下这样一段代码:

{ toLong() }

Doesn't make much sense, right? In fact, assigning this to a function type of (Int) -> Long - where Int is the (only) parameter, and the return type is Long - would rightfully result in a compilation error. You can fix this by simply qualifying the function call with the implicit single parameter it. However, for DSL building, this will cause a bunch of issues:

  • Nested blocks of DSL will have their upper layers shadowed:
    html { it.body { // how to access extensions of html here? } ... }
    This may not cause issues for a HTML DSL, but may for other use cases.
  • 它可以在代码中乱丢it个调用,特别是对于经常使用参数(即将成为接收方)的lambda.

这就是receivers开始发挥作用的地方.

通过将这段代码分配给一个函数类型,该函数类型将Int作为receiver(而不是参数!),代码突然编译:

val intToLong: Int.() -> Long = { toLong() }

Whats going on here?


一点旁注

This topic assumes familiarity with function types, but a little side note for receivers is needed.

Function types can also have one receiver, by prefixing it with the type and a dot. Examples:

Int.() -> Long  // taking an integer as receiver producing a long
String.(Long) -> String // taking a string as receiver and long as parameter producing a string
GUI.() -> Unit // taking an GUI and producing nothing

Such function types have their parameter list prefixed with the receiver type.


Resolving code with receivers

It is actually incredibly easy to understand how blocks of code with receivers are handled:

Imagine that, similar to extension functions, the block of code is evaluated inside the class of the receiver type. 100 effectively becomes amended by the receiver type.

对于我们前面的示例val intToLong: Int.() -> Long = { toLong() } ,它有效地导致代码块在不同的上下文中进行计算,就像它被放置在Int中的函数中一样.下面是一个使用手工制作的类型的不同示例,更好地展示了这一点:

class Bar

class Foo {
    fun transformToBar(): Bar = TODO()
}

val myBlockOfCodeWithReceiverFoo: (Foo).() -> Bar = { transformToBar() }

effectively becomes (in the mind, not code wise - you cannot actually extend classes on the JVM):

class Bar 

class Foo {
    fun transformToBar(): Bar = TODO()

    fun myBlockOfCode(): Bar { return transformToBar() }
}

val myBlockOfCodeWithReceiverFoo: (Foo) -> Bar = { it.myBlockOfCode() }

Notice how inside of a class, we don't need to use this to access transformToBar - the same thing happens in a block with a receiver.

碰巧的是,this上的文档还解释了如果当前代码块有两个接收器,如何通过qualified this使用最外层的接收器.


Wait, multiple receivers?

Yes. A block of code can have multiple receivers, but this currently has no expression in the type system. The only way to achieve this is via multiple higher-order functions that take a single receiver function type. Example:

class Foo
class Bar

fun Foo.functionInFoo(): Unit = TODO()
fun Bar.functionInBar(): Unit = TODO()

inline fun higherOrderFunctionTakingFoo(body: (Foo).() -> Unit) = body(Foo())
inline fun higherOrderFunctionTakingBar(body: (Bar).() -> Unit) = body(Bar())

fun example() {
    higherOrderFunctionTakingFoo {
        higherOrderFunctionTakingBar {
            functionInFoo()
            functionInBar()
        }
    }
}

Do note that if this feature of the Kotlin language seems inappropriate for your DSL, @DslMarker is your friend!


Conclusion

Why does all of this matter? With this knowledge:

  • 现在你明白了为什么你可以在一个数字的扩展函数中写toLong(),而不必以某种方式引用这个数字.Maybe your extension function shouldn't be an extension?
  • 你可以为你最喜欢的标记语言构建一个DSL,也许可以帮助解析其中一种(who needs regular expressions?!).
  • You understand why with, a standard library function and not a keyword, exists - the act of amending the scope of a block of code to save on redundant typing is so common, the language designers put it right in the standard library.
  • (maybe) you learned a bit about function types on the offshoot.

Kotlin相关问答推荐

来自SnapshotFlow的单元测试StateFlow仅发出initialValue

为什么在jacksonObjectMapper上将DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES设置为false无效?

如何访问嵌套在另一个 map 中的 map 中的值(在 kotlin 中)

Kotlin 中的as Long和.toLong()有什么区别?

如何使用成员引用在 Kotlin 中创建属性的分层路径

如何缩短 MaterialTheme.colors.primary?

为什么 KFunction2 在 Kotlin 中不是可表示类型?

Kotlin - 当表达式返回函数类型

在 Kotlin 中,当枚举类实现接口时,如何解决继承的声明冲突?

Kotlin 1.2.21 + SimpleXml 2.3.0 - consume List error (must mark set get method)

包括登录Elvis operator?

`DataBindingUtil` 中的 `bind`、`inflate` 和 `setContentView` 有什么区别

无法创建类 ViewModel kotlin 的实例

Kotlin 创建snackbar

在 Kotlin 中实现 (/inherit/~extend) 注解

如何在MVVM架构中观察RecyclerView适配器中的LiveData?

Kotlin/JS,Gradle 插件:无法加载@webpack-cli/serve命令

接口中的属性不能有支持字段

kotlin中密封类和密封接口的区别是什么

我应该在哪里调用 MobileAds.initialize()?