写点什么

Kotlin(1)-lambda 表达式和高阶函数操作符,java 面试资料推荐

用户头像
极客good
关注
发布于: 刚刚
  • 不依赖对象的全局函数。

  • 依赖对象的"类似"扩展函数。


两者都可以规定返回值类型(精通泛型的话,这里应该不难理解,泛型下一节详解)。


阅读源码:


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


细节


  1. with(a){} 大括号内的作用域里面,可以直接使用 当前 a 对象的引用,可以 this.xxx 也可以 a.xxx

  2. 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


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


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

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
Kotlin(1)-lambda表达式和高阶函数操作符,java面试资料推荐