Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby-on-rails-4/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Kotlin “a”是什么;“接收者”;在科特林?_Kotlin - Fatal编程技术网

Kotlin “a”是什么;“接收者”;在科特林?

Kotlin “a”是什么;“接收者”;在科特林?,kotlin,Kotlin,它与扩展功能有什么关系?为什么带有,而不是关键字 关于这个主题似乎没有明确的文档,只有关于知识的假设。的确,关于接收者概念的现有文档似乎很少(只有a),这是令人惊讶的: 他们的存在源于 它们在使用所述扩展功能中的作用 标准库函数的存在,在不了解接收者的情况下,可能看起来像一个关键词 a完全正确 所有这些主题都有文档,但没有深入介绍接收器 第一: 什么是接收器? Kotlin中的任何代码块都可能有一个(甚至多个)类型作为接收器,使接收器的功能和属性在该代码块中可用,而无需对其进行限定 想象一

它与扩展功能有什么关系?为什么
带有
,而不是关键字


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

的确,关于接收者概念的现有文档似乎很少(只有a),这是令人惊讶的:

  • 他们的存在源于
  • 它们在使用所述扩展功能中的作用
  • 标准库函数的存在,在不了解接收者的情况下,可能看起来像一个关键词
  • a完全正确
所有这些主题都有文档,但没有深入介绍接收器


第一:

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

想象一下这样的代码块:

{ toLong() }
没什么意义,对吧?事实上,将其分配给of
(Int)->Long
——其中
Int
是(唯一)参数,返回类型是
Long
——将正确地导致编译错误。您可以通过使用隐式单参数限定函数调用来解决此问题。但是,对于DSL构建,这将导致一系列问题:

  • DSL的嵌套块将对其上层进行阴影处理:
    html{it.body{//如何在此处访问html扩展?}…}

    这可能不会导致HTML DSL出现问题,但可能会导致其他用例出现问题
  • 它可以在
    It
    调用中丢弃代码,特别是对于经常使用参数(即将成为接收器)的lambda
这就是接受者发挥作用的地方

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

val intToLong: Int.() -> Long = { toLong() }
这是怎么回事


一点旁注 本主题假定读者熟悉,但需要对接受者稍加注意

函数类型也可以有一个接收器,方法是在其前面加上类型和点。示例:

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
此类函数类型的参数列表前缀为receiver类型


使用接收器解析代码 实际上,理解如何处理带有接收器的代码块非常容易:

想象一下,与扩展函数类似,代码块是在receiver类型的类中计算的。有效地被接收器类型修改。

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

class Bar

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

val myBlockOfCodeWithReceiverFoo: (Foo).() -> Bar = { transformToBar() }
实际上(在思想上,不是代码方面——您实际上无法在JVM上扩展类):

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

碰巧的是,上的文档还解释了如果当前代码块有两个接收器,如何通过


等等,多个接收器? 对。一个代码块可以有多个接收器,但当前在类型系统中没有表达式。归档的唯一方法是通过采用单一接收器功能类型的多个接收器。例如:

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,您的朋友就是您的朋友


结论 为什么这一切都很重要?有了这些知识:

  • 现在您了解了为什么可以在扩展函数中对数字编写
    toLong()
    ,而不必以某种方式引用数字
  • 您可以为您最喜欢的标记语言构建DSL,可能有助于解析其中一种(!)
  • 你知道为什么存在一个标准库函数而不是一个关键字-修改代码块的范围以节省冗余键入的行为是如此普遍,语言设计者将其放在标准库中
  • (可能)您在分支上了解了一些函数类型
这定义了一个
String.(->Unit
类型的变量,它告诉您

  • 字符串
    接收者
  • ()->单元
    是函数类型
如上所述,可以在方法体中调用此接收器的所有方法

因此,在我们的示例中,
这个
用于打印
字符串
。该函数可以通过写入

greet("Fitzgerald") // result is "Hello Fitzgerald"
上面的代码片段来自Simon Wirtz

Kotlin支持“具有接收者的函数文本”的概念。它允许访问主体中lambda的接收器的可见方法和属性,而无需任何附加限定符。这与扩展功能非常类似,在扩展功能中,还可以访问扩展中接收器对象的可见成员

一个简单的例子,也是Kotlin标准库中最大的函数之一,是
apply

public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
我们实例化一个
Bar
对象,并对其调用
apply
Bar
的实例成为“接收者”。在
{}
(lambda表达式)中作为参数传递的
,不需要使用其他限定符来访问和修改显示的可见属性
颜色
文本

带接收器的lambdas概念也是使用Kotlin编写DSL的最重要功能。

简单地说(没有任何e
greet("Fitzgerald") // result is "Hello Fitzgerald"
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
val foo: Bar = Bar().apply {
    color = RED
    text = "Foo"
}
 fun Foo.functionInFoo(): Unit = TODO()
 var greet: String.() -> Unit = { println("Hello $this") }
fun receiver_class.function_name() {
   //...
}
fun hasWhitespace(line: String): Boolean {
    for (ch in line) if (ch.isWhitespace()) return true
    return false
}
fun String.hasWhitespace(): Boolean {
    for (ch in this) if (ch.isWhitespace()) return true
    return false
}
        class Music(){
    
        var track:String=""
    
        fun printTrack():Unit{
            println(track)
        }
    }
    
    //Music class is the receiver of this function, in other words, the lambda can be piled after a Music class just like its extension function Since Music is an instance, refer to it by 'this', refer to lambda parameters by 'it', like always
    val track_name:Music.(String)->Unit={track=it;printTrack()}
/*Create an Instance of Music and immediately call its function received by the name 'track_name', and exclusively available to instances of this class*/
Music().track_name("Still Breathing")

//Output
Still Breathing