Kotlin(1)-lambda 表达式和高阶函数操作符,java 面试资料推荐
不依赖对象的全局函数。
依赖对象的"类似"扩展函数。
两者都可以规定返回值类型(精通泛型的话,这里应该不难理解,泛型下一节详解)。
阅读源码:
@kotlin.internal.InlineOnlypublic inline fun <R> run(block: () -> R): R {contract {callsInPlace(block, InvocationKind.EXACTLY_ONCE)}return block()}
@kotlin.internal.InlineOnlypublic inline fun <T, R> T.run(block: T.() -> R): R {contract {callsInPlace(block, InvocationKind.EXACTLY_ONCE)}return block()}
run 函数被重载,参数有所不同
前者 参数类型为 ()->R ,返回值为 R ,函数体内执行 block(),并且返回执行结果
后者 参数类型为 T.()->R ,返回值为 R , T.() 明显是依赖 T 类的(貌似 T 的扩展函数),返回值依然是 R,执行完之后返回结果。
并且,可以发现 两者都是内联函数 inline (执行代码时不进行方法的压栈出栈,而是类似于直接在目标处执行代码段)
所以,前者不需要依赖对象,后者必须依赖对象(因为它是 T 类的"扩展函数")
使用场景
根据 run 方法的特性,无论是不是依赖对象,它都是封装了一段代码,而且还是 inline 的。所以:
如果你不想把一段代码独立成方法,又不想让它们看上去这么凌乱,最重要的是告诉后来的开发者 这段代码是一个整体,不要随便给我在里面插代码,或者拆分。那就用 run 方法,把他们整合到一个作用域中。
run {println("这是一段代码逻辑很相近的代码段")println("但是我不想把他独立成一个方法")println("又担心别人会随便改")println("所以用 run 方法把他们放在同一个作用域中")println("小组中其他人看到这段,就知道不要把无关代码插进来")}
更神奇的是,这个 run 函数是有返回值的,参数返回值可以利用起来:
fun testRun2(param1: String, param2: String, param3: String) {//我想让这几个参数都不为空,如果检查是空,就不执行方法主体 val checkRes: Boolean = run<Boolean> {when {param1.isNullOrEmpty() -> {false}param2.isNullOrEmpty() -> {false}param3.isNullOrEmpty() -> {false}else -> true}}
if (checkRes){println("参数检查完毕,现在可以继续接下来的操作了")}else{println("参数检查不通过,不执行主体代码")}}fun main() {testRun2("1", "2", "")}
main 运行结果:
小结论
run 方法在整合小段代码的功效上,还是很实用的
其他高阶函数
上面列举出来的这些系统高阶函数原理上都差不多,只是使用场景有区别,因此除了 run 之外,其他的不再详解,而只说明使用场景。
apply
和 run 只有一个区别,run 是返回 block()执行之后的返回值,而,apply 则是返回 this,因此 apply 必须依赖对象。而由于返回了 this,因此可以连续 apply 调用。
fun testApply() {val a = A()a.apply {println("如果一个对象要对它进行多个阶段的处理")}.apply {println("那么多个阶段都挤在一起则容易混淆,")}.apply {println("此时可以用 apply 将每一个阶段分开摆放")}.apply {println("让程序代码更加优雅")}}
fun main() {testApply()}
with
下面的代码应该都很眼熟,Glide 图片加载框架的用法,with(context)然后链式调用
Glide.with(this).load(image).asGif().into(mImageView);
Kotlin 中的 with 貌似把这一写法发扬光大了(只是类比,不用深究),场景如下:
class A {val a = 1val b = "b"
fun showA() {println("$a")}
fun showB() {println("b")}}
fun testWith() {val a = A()with(a) {println("作用域中可以直接引用创建出的 a 对象")this.athis.bthis.showA()this}.showB()}
fun main() {testWith()}
细节
with(a){} 大括号内的作用域里面,可以直接使用 当前 a 对象的引用,可以 this.xxx 也可以 a.xxx
with(a){} 大括号作用域内的最后一行是 返回值,如果我返回 this,那么 with 结束之后,我可以继续 调用 a 的方法
also
also 和 with 一样,必须依赖对象,返回值为 this。因此它也支持链式调用,它和 apply 的区别是:
apply 的 block,没有参数,但是 also 则将 this 作为了参数。这么做造成的差异是:
作用域 { } 内调用当前对象的方式不同。
class A {val a = 1val b = "b"
fun showA() {println("$a")}
fun showB() {println("b")}}fun testApply() {A().apply {this.showA()println("=======")}.showB()}
fun testAlso() {A().also {it.showA()println("=======")}.showB()}
apply 必须用this.xxx
, also 则用 it.xxx
.
let
类比到 run:
public inline fun <T, R> T.run(block: T.() -> R): R {return block()}public inline fun <T, R> T.let(block: (T) -> R): R {return block(this)}
只有一个区别:run 的 block 执行不需要参数,而 let 的 block 执行时需要传入 this。
造成差异为:
A().run {println("最后一行为返回值")this}.showA()
A().let {println("最后一行为返回值")it}.showA()
run{} 作用域中 只能通过 this.xxx 操作当前对象,let 则用 it.xxx
takeif 和 takeunless
这两个作用相反,并且他们必须依赖对象。看源码:
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {return if (predicate(this)) this else null}public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {return if (!predicate(this)) this else null}
predicate 是 (T)->Boolean 类型的 lambda 表达式,表示断言判断,如果判断为 true,则返回自身,否则返回空
class A {val a = 0val b = "b"
fun showA() {println("$a")}
fun showB() {println("b")}}
fun testTakeIfAndTakeUnl
ess() {println("test takeIf")A().takeIf { it.a > 0 }?.showB()println("==========")println("test takeUnless")A().takeUnless { it.a > 0 }?.showB()}
fun main() {testTakeIfAndTakeUnless()}
执行结果:
test takeIf
test takeUnless0 b
takeIf / takeUnless
适用于将条件判断的代码包在一个作用域{}中,然后 用 ?.xxxx 的安全调用符 来 执行对象操作。
repeat
repeat 是 多次循环的傻瓜版写法。
fun testRepeat() {repeat(10) {print("$it ")}}
fun main() {testRepeat()}
执行结果:
0 1 2 3 4 5 6 7 8 9
lazy
lazy 的作用是: 延迟初始化 val 定义的常量。
class B {val i: Int by lazy {println("执行 i 初始化")20}
init {println("构造函数执行")}}
如果只是初始化 B 对象,却没有使用到变量i
, 那么延迟初始化不会执行。
fun main() {B()}
执行结果:
构造函数执行
而如果使用到了变量i
,才会去在调用之前初始化:
fun main() {println("i 变量的值是:" + B().i)}
评论