搞定Receiver

Kotlin官方文档中没有针对Receiver的专题讲解,但这个知识点却贯穿在了整个基础体系中。这节课我们将彻底揭开Receiver的神秘面纱,带你一探究竟。

什么是Receiver

解释这个概念之前,先想一想为什么会有这个东西,在Java语言中是否存在相似的概念。直接来看一个例子:

这是Java语言中非常常见的一段代码,将数字字符串转换为Long。如果使用Kotlin实现类似的需求,我们会怎么做的呢?看代码:

干脆利落!事实上,JDK的String类并没有toLong方法。之所以能够如此优雅地将字符串转换为Long主要归功于Kotlin的扩展语法。看一眼toLong的官方实现:

可以看到,toLong方法使用了String前缀标识,这意味着toLong方法是为String类扩展的。在Kotlin语言中,你将可以直接使用点语法调用这个扩展方法,String就称之为toLong方法的Receiver。同样地,如果toLong前缀是Int,Int就称之为toLong方法的Receiver。

那么,问题来了。Java语言中并没有扩展语法,在Java语言中如何调用toLong方法呢?

这里恰好用到了Receiver的概念,Kotlin编译器会生成一个与声明扩展的文件名加Kt为名称的类,扩展方法将作为该类的静态方法,调用的时候要求传入Receiver。因此,在Java端调用toLong可以这样做:

实际上,在Kotlin语言中,类不再是语言的最小单位。我们既可以单独声明一个全局函数,也可以声明全局变量。因此,你可以认为toLong是一个函数整体,这个函数的接收者可以是任意对象。

而对于Java,Java语言中所有的行为都必须在类体中完成。具体到某个函数或某一个变量始终属于某一个类实例。换而言之,其Receiver是固定的,也就没有了所谓Receiver的概念。

多个Receiver

事实上,在Kotlin语言中,同一个函数可能存在多个Receiver。那么,什么时候会出现这种情况呢?

在扩展声明中,如果对应的前缀本身是一个泛型,就意味着当前函数的Receiver可能是满足泛型要求的任意对象。尤其如果是这样声明的话:

这就意味着,所有类都是apply方法的Receiver,具体类型取决于你的调用对象。例如:如果这样调用,当前apply的Receiver就是Dog。

理解上下文

这里说的上下文其实和JS里面的上下文很像。刚刚学习Kotlin的同学容易产生一个困惑,在扩展方法中如何调用Receiver类中的方法。事实上,你的扩展方法恰好就处于Receiver实例的上下文环境中,你可以直接调用该Receiver类中的任意可访问方法。

理解了上述概念后,再回到标准库中的方法讲解。仔细观察这几个方法,你会发现他们有一个共同点,都执行了一个lambda表达式(repeat除外)。所不同的只有下面两个地方:

Receiver 有的函数Receiver是当前实例;有的函数可以自由指定Receiver;有的函数将Receiver作为参数传入block,这决定了当前block的上下文,

block的类型不一样,有的block有返回值,有的没有返回值。有的返回Receiver本身,有的可以交给开发者指定。

按照这个思路去理解这几个标准函数的用法,一切都变得明朗起来。

关注欧阳锋工作室,与锋同行

欧阳锋工作室

欢迎加入Kotlin交流群

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180426A1GV8D00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券