Kotlin| 作用域函数

Kotlin有5个作用域函数,分别是let{},run{},with{},apply{}also{},它们都可以接收并且执行一个lamda表达式,去掉lamda表达式的argument列表和{},看上去就像在执行代码块 (KClosure)。抛开Kotlin实现它们的动机和原理,我的目标就是学会怎么用它们!

不知道如何选择?
首先看Kotlin这一部分的guide,发现首先要搞懂下面两个简单的概念:
1️⃣上下文对象 (Context Object): this versus it
2️⃣返回结果 (Result): lamda versus object

初学编程应该都知道C风格语言喜欢用一对{}表示一个作用域 (Scope),也应该清楚lamda表达式返回结果是最后一行代码/表达式的执行结果。

上下文对象就是调用作用域函数的对象,以let为例就是someObject.let{}里的someObject,而作用域函数内部的lamda表达式要持有someObject的一份引用,那么有两种方式,一是lamda接收对象的this关键字,二是lamda表达式的argument。
原形:

someObject.let {it->}

变体:

someObject.let{}

返回结果要么就是lamda表达式的结果,要么是对象本身。如果是lamda结果,就代表这中间做了一些额外的计算或任务,然后可以赋值给一个局部变量;如果指向对象本身,那就是对象内部成员的配置或操作,然后可以继续调用对象方法或return语句/直接返回。

// lamda结果赋值给firstNum变量
val numbers = mutableListOf("one", "two", "three", "four", "five")
val firstNum = numbers.first().let{firstItem -> if (firstItem.length >= 5) firstItem else "!" + firstItem + "!"}

// adam是一个Person对象
val adam = Person("Adam").apply {
    age = 32
    city = "London"        
}

另外,还需要了解一件事,大多数作用域函数都是inline拓展函数(let不是inlne),极个别情况下也可以是inline单独函数,这2个极个别的例外就是with和没有接收对象 (前面没有someObject.)的run。

fun <T, R> T.let(f: (T) -> R): R = f(this) // let原型

// also原型
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

// run独立函数
val decimalRegex = run {
    val digits = "0-9"
    val sign = "+-"

    Regex("?[$digits$hexDigits]+")
}

for (match in decimalRegex.findAll("+1234")) {
    println(match.value)
}

其他原型看这位简诱的整理

最后下表根据这两个维度对它们进行了分类。

上下文 / 结果 lamda结果 (赋值) 自己 (返回)
it (可重命名) let(拓展) also(拓展)
this (可省略) run(拓展/单独函数), with(单独函数) apply(拓展)

最后的最后,可耻地搬运Kotlin文档的建议

  • 在非空对象执行lamda:let
  • 在{}写lamda: someList.filter().map{ it.length }.filter { it > 3 }.let{::println}
  • 设置对象属性:Person("张三").apply { type="法外狂徒" }
  • 设置对象属性+调用成员方法:val sum = Pair(1,2).run { operator = "+" compute(x,y,operator) }
  • 定义局部表达式:val expression = run { Regex(".+") }
  • 日志打印或调试之类不修改对象的操作:
val numbers = mutableListOf("one", "two", "three")
numbers.also { println("新增前: $it") }.add("four")
  • 在对像上调用一系列业务函数:
val zhangsan = Person()
with(zhangsan) {
  doQiangjian(zhangsan) // 张三强X妇女
  doFeifajizi(zhangsan) // 张三非法集资
}

补充:takeIf()takeUnless()接受一个Predicate然后返回null或对象,然后可以组合作用于函数继续链式调用,其中takeUnless表示除......之外。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容

  • 什么是作用域函数(Scope Functions)? Kotlin 标准库包含了几个特殊的函数,其目的是在调用对象...
    SkyRiN阅读 867评论 1 4
  • 前言 最近使用kotlin语言开发了新的项目,kotlin的一些特性和大量的语法糖相当好用,相比于java,开发效...
    SirWwh阅读 2,305评论 1 2
  • kotlin中经常出现run、with、let、also、apply,开始时候容易迷糊,有什么用有什么区别? 一:...
    追梦者wang阅读 580评论 0 1
  • 上面是常用的五个作用域函数 run let with apply also从定义上我们看出apply 和 also...
    莫库施勒阅读 494评论 0 0
  • 先来看一段代码 "result."频繁出现 如何去掉繁琐的出现的变量代码我们可以在赋值时增加代码块 {return...
    gaom明阅读 342评论 0 0