什么是“接收者"?在科特林? [英] What is a "receiver" in Kotlin?

查看:141
本文介绍了什么是“接收者"?在科特林?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

它与扩展功能有什么关系?为什么with 一个函数,而不是关键字?

How is it related to extension functions? Why is with a function, not a keyword?

该主题似乎没有明确的文档,只有关于扩展的知识假设. .

There appears to be no explicit documentation for this topic, only the assumption of knowledge in reference to extensions.

推荐答案

的确,关于接收器概念的现有文档似乎很少(只有

It is true that there appears to be little existing documentation for the concept of receivers (only a small side note related to extension functions), which is surprising given:

  • their existence springing out of extension functions;
  • their role in building a DSL using said extension functions;
  • the existance of a standard library function with, which given no knowledge of receivers might look like a keyword;
  • a completely seperate syntax for function types.

所有这些主题都有文档,但是在接收者上没有深入的介绍.

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

第一:

Kotlin中的任何代码块都可能具有(甚至是多种)类型作为接收器,从而使接收器的功能和属性在该代码块中可用,而没有资格对其进行限定.

Any block of code in Kotlin may have a (or even multiple) types as a receiver, making functions and properties of the receiver available in that block of code without qualifiying it.

想象一下这样的代码块:

Imagine a block of code like this:

{ toLong() }

不是很有意义,对吧?实际上,将其分配给(Int) -> Long函数类型-其中Int是(唯一)参数,返回类型为Long-正确地会导致编译错误.您可以通过仅使用隐式单个参数 it .但是,对于DSL构建,这会引起很多问题:

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:

  • 嵌套的DSL块的上层将被遮盖:
    html { it.body { // how to access extensions of html here? } ... }
    这可能不会导致HTML DSL出现问题,但可能会导致其他用例.
  • 它可以通过it调用来填充代码,尤其是对于大量使用其参数(即将成为接收者)的lambda.
  • 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 can litter the code with it calls, especially for lambdas that use their parameter (soon to be receiver) a lot.

这是接收者发挥作用的地方.

通过将此代码块分配给以Int作为接收器(而不是参数!)的函数类型,代码突然编译:

By assigning this block of code to a function type that has Int as a receiver (not as a parameter!), the code suddenly compiles:

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

这是怎么回事?

本主题假定您熟悉函数类型,但有些不足需要收件人注意.

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

函数类型也可以具有 one 接收器,方法是在函数类型前面加上一个点和一个点.例子:

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.

了解接收方代码块的处理方式实际上非常容易:

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

想象一下,类似于扩展功能,代码块是在接收器类型的类中求值的. 实际上已被接收者类型修改.

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

对于我们前面的示例val intToLong: Int.() -> Long = { toLong() } ,它实际上导致在不同的上下文中评估代码块,就像将其放在Int内部的函数中一样.这是一个使用手工制作的类型的示例,可以更好地展示这一点:

For our earlier example, val intToLong: Int.() -> Long = { toLong() } , it effectively results in the block of code being evaluated in a different context, as if it was placed in a function inside Int. Here's a different example using handcrafted types that showcases this better:

class Bar

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

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

有效地变为(在头脑中,不是代码明智的,您实际上不能在JVM上扩展类):

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() }

请注意,在类内部,我们不需要使用this来访问transformToBar-在具有接收器的块中也会发生相同的情况.

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.

碰巧的是,上的文档也解释了如何使用最外部的接收器(如果当前代码块有两个接收器),可以通过对此进行限定.

It just so happens that the documentation on this also explains how to use an outermost receiver if the current block of code has two receivers, via a qualified this.

是的.代码块可以具有多个接收器,但是当前在类型系统中没有表达式.存档的唯一方法是通过多个高阶函数,它们采用单个接收器函数类型.示例:

Yes. A block of code can have multiple receivers, but this currently has no expression in the type system. The only way to archieve 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()
        }
    }
}

请注意,如果Kotlin语言的此功能似乎不适合您的DSL,请 @ DslMarker 是您的朋友!

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

为什么所有这些都很重要?有了这些知识:

Why does all of this matter? With this knowledge:

  • 现在您了解了为什么可以在扩展功能上对数字写toLong(),而不必以某种方式引用该数字. 也许您的扩展功能不应该是扩展吗?
  • 您可以为自己喜欢的标记语言构建DSL,也许可以帮助解析其中一种(谁需要正则表达式?!).
  • 您了解为什么 with (标准库)存在功能而不是关键字-修改代码块范围以节省冗余键入的行为非常普遍,语言设计人员将其正确地放在了标准库中.
  • (也许)您在分支中学到了一些有关函数类型的知识.
  • you now understand why you can write toLong() in an extension function on a number, instead of having to reference the number somehow. Maybe your extension function shouldn't be an extension?
  • You can build a DSL for your favorite markup language, maybe help parsing the one or other (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 redudant 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.

这篇关于什么是“接收者"?在科特林?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆