自己实现kotlin let,apply函数,一步一步简单直观,再也不晕

在Kotlin当中,我们经常会用到一些扩展函数,比如let,可以用来判空之后的操作,还有apply可以用来做glide或者okhttp这种建造者模式,直接写属性设置就行,不需要重复写变量名。
那么这些方法是怎么实现的呢,我们先来看看源码

public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

是不是蒙了,这是什么玩意儿啊,写的每个单词都认识,连一起咋呀看不懂。
我看这个函数看了很多边,解构过好几次,绕来绕去,最后总是把自己绕晕,咋也理解不了。
最后我想了想,尝试自己去实现一个let函数,等实现了,也算是搞懂了这几个函数的逻辑,下面来一步步把我的实现思路写一下
首先,让我们先写一个简单的扩展函数

fun Int.test(a:String):String{
        return a
    }

这就是给Int加了一个名字为test的扩展函数,参数和返回值都为String类型。
然后,我们把参数a改一下,改成一个参数为Int返回值是String的函数

fun Int.test(a:(b:Int)->String):String{
        return a(this)
    }

因为a方法的返回值是String类型,所以test方法的返回值可以直接使用a方法的返回值,a的参数是Int,可以直接用当前Int对象的值,也就是a(this)。
这时候我们把Int类型和String类型,替换成泛型

fun <T,R>T.test(a:(b:T)->R):R{
        return a(this)
    }

//对比两个方法,是不是就一致了。
//inline的功能大家自行了解,不影响逻辑
public inline fun <T, R> T.let(block: (T) -> R): R {
//这个方法类似断言,对逻辑本身没有影响
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

如果要调用我们可以这么写

//先定义一个参数为Int,返回类型为String的方法
fun c(it:Int):String{
        return it+1
    }
//调用
1.test(::c)
//这时候变成lambda函数
1.test{
//it是lambda自己提供的默认变量
            "变量经过处理之后:$it"
        }

调用可以这么理解,一个叫test的方法有两个参数,一个是要操作的对象,也就是1,一个是要怎么操作,也就是中括号内的内容(可以当成是匿名函数)。
这时候,把with的方法拿出来

public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

是不是就跟let是一个东西了,上面的变量1对应了receiver参数,只不过写法不一样。当然这些东西本来也就是语法糖,本质上就是一个静态方法。写起来方便就是最大的优势。比如可以这么写

1.test {  }.test {  }.test {  }.test {  }

可以无限级联,with是做不到的,这种无限级联的是不是就会让我们联想到list的操作函数,filter,map,forEach等方法。逻辑其实都是类似的,只不过理解起来有点绕。

当然还有一个类型,apply

public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

老规矩,从简单的来

fun Int.test2(a:Unit):Int{
        return this
    }

替换a为一个没有返回值和入参的方法

 fun Int.test2(a:()->Unit):Int{
//传接进来的方法需要调用
      a()
        return this
    }
//这时候如果我们调用,可以这么写
fun c(){  }
test2(::c)

这时候我们会发现方法c里面没有办法调用到Int对象本身的方法,咋办,把c变成Int的扩展方法

fun Int.c(){}
//调用
test2(Int.c())
//那么对应的,test2内的参数需要限定为Int的方法
 fun Int.test2(a:Int.()->Unit):Int{
        a()
        return this
    }

替换泛型

fun <T> T.test2(a:T.()->Unit):T{
            a()
            return this
        }
//调用
val str= "abc"
        str.test2 { substring(0,1)
        split("a")//注意这里所有的调用都是对"abc"的操作,因为字符串的唯一性,所有的操作的结果在          
 //这里无意义,返回的还是"abc"本身。所以对字符串的操作大家还是别用这个函数了。
}
//对比,返回值其实是可有可无的。
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}
//如果返回值与本身不同,就变成了了run方法
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

apply函数可以理解成test2有两个参数,一个是对象本身,一个是一个对象的扩展函数。

总结--apply和let最大的区别就是

let会把对象自身当成参数传给了后面的方法,所以在lambda变形的情况下,调用要用到it代表对象本身。
apply 是限定了传入的方法必须为对象存在的方法,所以可以直接用this调用,当然this可以省略。
怎么感觉还是好绕口。。。算了,大家自己写写,慢慢理解吧!!

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

推荐阅读更多精彩内容