Kotlin let,run,apply等扩展函数原理

kotlin基本函数的使用

高阶扩展函数

1.let函数
let的使用

在闭包内用it代替调用者,let的返回值为函数的最后一行。
let常用来做为判空后的处理

        //不用let时每次都需要对textView判空
        textView?.text = "let"
        textView?.setTextColor(Color.BLACK)
        textView?.ellipsize = TextUtils.TruncateAt.END

        textView?.let {
            it.text = "let"
            it.setTextColor(Color.BLACK)
            it.ellipsize = TextUtils.TruncateAt.END
        }
let的定义
//定义两个泛型T,R,T.let代表对任意类T添加let扩展函数,这个扩展函数的返回值为R
//参数block是一个函数且类型为(T) -> R
//由于let返回值为R且block返回值也为R,所以直接返回block函数的返回值
//调用let的是T,所以let闭包里this就代表T,block的参数为T,block应传入this,所以在block闭包内使用it即代表T
public inline fun <T, R> T.let(block: (T) -> R): R {
    return block(this)
}

上面可能有些人看不太明白,我们把泛型去了再看会发现其实挺简单的
这里定义String的扩展函数let1,返回Int类型

//去掉泛型后的let
public inline fun String.let1(block: (String) -> Int): Int {
    return block(this)
}

我们分别调用let1和let其结果是一样的

        //这里给出调用let1时的简化过程,对函数有不太理解的可以去看看上一篇文章
        var a: Int

        a = "1".let1(fun(a: String): Int {
            return a.toInt()
        })
        Log.i("kotlin fun  test2:  ", "返回值$a")

        a = "1".let1({ a ->
            a.toInt()
        })
        Log.i("kotlin fun  test2:  ", "返回值$a")

        a = "1".let1 { a ->
            a.toInt()
        }
        Log.i("kotlin fun  test2:  ", "返回值$a")

        //这里就是我们熟悉的let调用方式
        a = "1".let1 {
            it.toInt()
        }
        Log.i("kotlin fun  test2:  ", "返回值$a")

        //然后我们直接调用let
        a = "1".let {
            it.toInt()
        }
        Log.i("kotlin fun  test2:  ", "返回值$a")

以上的几个函数结果都是相同的

2.run函数

相对于let,我更喜欢使用run,一般情况下let能做的事用run也可以做,而且用run代码会更简洁。

run的使用

在闭包内用this代替调用者,run的返回值为函数的最后一行。

        //相对于let,代码简洁
        textView?.run {
            text = "run"
            setTextColor(Color.BLACK)
            ellipsize = TextUtils.TruncateAt.END
        }
run的定义

相对于let,run的改动点只有一处,由(T)换成了T.()

//定义两个泛型T,R,T.run代表对任意类T添加run扩展函数,这个扩展函数的返回值为R
//参数block是一个函数且类型为T.() -> R
//由于run返回值为R且block返回值也为R,所以直接返回block函数的返回值
//T.()代表给T添加一个匿名扩展函数,即block函数是T的匿名扩展函数,所以block函数闭包内的this即代表T
public inline fun <T, R> T.run(block: T.() -> R): R {
    return block()
}

上面注释前三行和let一样,只有最后一行有区别,这也是导致let和run差异的原因。

3.with函数

with函数在实际项目中应用很少,基本都被run代替,这里还是介绍下with函数的原理

with的使用

with用法和let、run有所不同,with不是作为扩展函数使用的,with接收一个对象,闭包内this代表传入的对象,with的返回值为函数的最后一行。

        with(textView) {
            if (this != null) {
                text = "with"
                setTextColor(Color.BLACK)
                ellipsize = TextUtils.TruncateAt.END
            }
        }
with的定义
//with和run的区别在于,run是通过扩展函数实现,with通过顶层函数实现(不借助类或对象可以直接使用)
//由于with是顶层函数,闭包内不包含上下文,所以调用block函数时需要指定T的对象进行调用,这也导致了with相对于run需要多传入一个参数T
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    return receiver.block()
}
4.apply函数

apply和run都是项目中最常见的扩展函数,run用来返回闭包内最后一行,apply用来返回调用者对象。

apply的使用

apply最常用的有两个地方
1.给对象进行初始化赋值
2.给model进行数据判空处理

        //1
        var textView = findViewById<TextView>(R.id.btn).apply {
            text = "let"
            setTextColor(Color.BLACK)
            ellipsize = TextUtils.TruncateAt.END
        }
        //2
        userInfo?.apply {
            //userInfo不为空时要处理的内容
        }?.info?.apply {
            //info不为空时要处理的内容
        }?.title?.apply {
            //title不为空时要处理的内容
        }
class UserInfo {
    var name: String? = null
    var info: Info? = null

    class Info {
        var title: String? = null
    }
}
apply的定义
//相较于run,apply只有一个泛型,即apply是T的扩展函数,返回的也是T
//block依旧为T的匿名扩展函数,apply闭包包含T的上下文关系,直接在闭包调用block,返回this
public inline fun <T> T.apply(block: T.() -> Unit): T {
    block()
    return this
}
5.also函数

also与with一样,在实际项目中应用较少,这里也只简单介绍下

also的使用
        textView?.also {
            it.text = "also"
            it.setTextColor(Color.BLACK)
            it.ellipsize = TextUtils.TruncateAt.END
        }
also的定义
//also是T的扩展函数,所以also的闭包的上下文是T
//匿名函数block的入参为T,所以block传入this
//block是非扩展函数,所以block的闭包上下文为also的外层对象,在block的闭包里用it指向T
public inline fun <T> T.also(block: (T) -> Unit): T {
    block(this)
    return this
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容