Kotlin:高阶函数,2024年最新前端app面试题

例:都要获取字符串的长度。

val index = 3
val num = run {
when(index){
0 -> “kotlin”
1 -> “java”
2 -> “php”
3 -> “javaScript”
else -> “none”
}
}.length
println(“num = $num”)

输出结果为:

num = 10

3.2.2、T.run()

其实T.run()函数和run()函数差不多,关于这两者之间的差别我们看看其源码实现就明白了:

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

从源码中我们可以看出,block()这个函数参数是一个扩展在T类型下的函数。这说明我的block()函数可以可以使用当前对象的上下文。所以当我们传入的lambda表达式想要使用当前对象的上下文的时候,我们可以使用这个函数。

例:

val str = “kotlin”
str.run {
println( “length =  t h i s . l e n g t h "   ) p r i n t l n (   " f i r s t   =   {this.length}" ) println( "first =  this.length)println( "first = {first()}”)
println( “last = ${last()}” )
}

输出结果为:

length = 6
first = k
last = n

在其中,可以使用this关键字,因为在这里它就代码str这个对象,也可以省略。因为在源码中我们就可以看出,block()就是一个T类型的扩展函数。

这在实际的开发当中我们可以这样用:

例: 为TextView设置属性。

val mTvBtn = findViewById(R.id.text)
mTvBtn.run{
text = “kotlin”
textSize = 13f

}

3.3 、with()函数

其实with()函数和T.run()函数的作用是相同的,我们这里看下其实现源码:

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

这两个函数的区别在于:

  1. with是正常的高阶函数,T.run()是扩展的高阶函数。
  2. with函数的返回值指定了receiver为接收者。

例:实现上面的T.run()函数的列子

val str = “kotlin”
with(str) {
println( “length =  t h i s . l e n g t h "   ) p r i n t l n (   " f i r s t   =   {this.length}" ) println( "first =  this.length)println( "first = {first()}”)
println( “last = ${last()}” )
}

输出结果为:

length = 6
first = k
last = n

例:当我的对象可为null的时候,看两个函数之间的便利性

val newStr : String? = “kotlin”

with(newStr){
println( “length =  t h i s ? . l e n g t h "   ) p r i n t l n (   " f i r s t   =   {this?.length}" ) println( "first =  this?.length)println( "first = {this?.first()}”)
println( “last = ${this?.last()}” )
}

newStr?.run {
println( “length =  l e n g t h "   ) p r i n t l n (   " f i r s t   =   length" ) println( "first =  length)println( "first = {first()}”)
println( “last = ${last()}” )
}

从上面的代码我们就可以看出,当我们使用对象可为null时,使用T.run()比使用with()函数从代码的可读性与简洁性来说要好一些。当然关于怎样去选择使用这两个函数,就得根据实际的需求以及自己的喜好了。

3.4、T.apply()函数

我们先看下T.apply()函数的源码:

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

T.apply()源码中在结合前面提到的T.run()函数的源码我们可以得出,这两个函数的逻辑差不多,唯一的区别是T,apply执行完了block()函数后,返回了自身对象。而T.run是返回了执行的结果。

故而: T.apply的作用除了实现能实现T.run函数的作用外,还可以后续的再对此操作。下面我们看一个例子

例:为TextView设置属性后,再设置点击事件等

val mTvBtn = findViewById(R.id.text)
mTvBtn.apply{
text = “kotlin”
textSize = 13f

}.apply{
// 这里可以继续去设置属性或一些TextView的其他一些操作
}.apply{
setOnClickListener{ … }
}

或者:设置为Fragment设置数据传递

// 原始方法
fun newInstance(id : Int , name : String , age : Int) : MimeFragment{
val fragment = MimeFragment()
fragment.arguments?.putInt(“id”,id)
fragment.arguments?.putString(“name”,name)
fragment.arguments?.putInt(“age”,age)

return fragment
}

// 改进方法
fun newInstance(id : Int , name : String , age : Int) = MimeFragment().apply {
arguments = Bundle()
arguments?.putInt(“id”,id)
arguments?.putString(“name”,name)
arguments?.putInt(“age”,age)
}

3.5、T.also()函数

关于T.also函数来说,它和T.apply很相似,。我们先看看其源码的实现:

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

从上面的源码在结合T.apply函数的源码我们可以看出: T.also函数中的参数block函数传入了自身对象。故而这个函数的作用是用用block函数调用自身对象,最后在返回自身对象

这里举例一个简单的例子,并用实例说明其和T.apply的区别

例:

“kotlin”.also {
println(“结果:KaTeX parse error: Expected 'EOF', got '}' at position 24: …("-java")}") }̲.also { print…{it.plus(”-php")}")
}

“kotlin”.apply {
println(“结果:KaTeX parse error: Expected 'EOF', got '}' at position 26: …("-java")}") }̲.apply { prin…{this.plus(”-php")}")
}

他们的输出结果是相同的:

结果:kotlin-java
结果:kotlin-php

结果:kotlin-java
结果:kotlin-php

从上面的实例我们可以看出,他们的区别在于,T.also中只能使用it调用自身,而T.apply中只能使用this调用自身。因为在源码中T.also是执行block(this)后在返回自身。而T.apply是执行block()后在返回自身。这就是为什么在一些函数中可以使用it,而一些函数中只能使用this的关键所在

3.6、T.let()函数

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

从上面的源码中我们可以得出,它其实和T.also以及T.apply都很相似。而T.let的作用也不仅仅在使用空安全这一个点上。用T.let也可实现其他操作

例:

“kotlin”.let {
println(“原字符串:KaTeX parse error: Expected 'EOF', got '}' at position 42: …t.reversed() }̲.let { printl…it”)     // niltok
it.plus(“-java”)
}.let {
println(“新的字符串:$it”)          // niltok-java
}

“kotlin”.also {
println(“原字符串:KaTeX parse error: Expected 'EOF', got '}' at position 38: …t.reversed() }̲.also { print…it”)     // kotlin
it.plus(“-java”)
}.also {
println(“新的字符串:$it”)        // kotlin
}

“kotlin”.apply {
println(“原字符串:KaTeX parse error: Expected 'EOF', got '}' at position 42: …s.reversed() }̲.apply { prin…this”)     // kotlin
this.plus(“-java”)
}.apply {
println(“新的字符串:$this”)        // kotlin
}

输出结果看是否和注释的结果一样呢:

原字符串:kotlin
反转字符串后的值:niltok
新的字符串:niltok-java

原字符串:kotlin
反转字符串后的值:kotlin
新的字符串:kotlin

原字符串:kotlin
反转字符串后的值:kotlin
新的字符串:kotlin

3.7、T.takeIf()函数

从函数的名字我们可以看出,这是一个关于条件判断的函数,我们在看其源码实现:

public inline fun  T.takeIf(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (predicate(this)) this else null
}

从源码中我们可以得出这个函数的作用是:传入一个你希望的一个条件,如果对象符合你的条件则返回自身,反之,则返回null

例: 判断一个字符串是否由某一个字符起始,若条件成立则返回自身,反之,则返回null

val str = “kotlin”

val result = str.takeIf {
it.startsWith(“ko”) 
}

println(“result = $result”)

输出结果为:

result = kotlin

3.8、T.takeUnless()函数

这个函数的作用和T.takeIf()函数的作用是一样的。只是和其的逻辑是相反的。即:传入一个你希望的一个条件,如果对象符合你的条件则返回null,反之,则返回自身。

这里看一看它的源码就明白了。

public inline fun  T.takeUnless(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (!predicate(this)) this else null
}

这里就举和T.takeIf()函数中一样的例子,看他的结果和T.takeIf()中的结果是不是相反的。

例:

val str = “kotlin”

val result = str.takeUnless {
it.startsWith(“ko”) 
}

println(“result = $result”)

输出结果为:

result = null

3.8、repeat()函数

首先,我们从这个函数名就可以看出是关于重复相关的一个函数,再看起源码,从源码的实现来说明这个函数的作用:

public inline fun repeat(times: Int, action: (Int) -> Unit) {
contract { callsInPlace(action) }

for (index in 0…times - 1) {
action(index)
}
}

从上面的代码我们可以看出这个函数的作用是:根据传入的重复次数去重复执行一个我们想要的动作(函数)

例:

repeat(5){ println(“我是重复的第 i t   +   1 次,我的索引为: {it + 1}次,我的索引为: it + 1次,我的索引为:it”) }

输出结果为:

我是重复的第1次,我的索引为:0
我是重复的第2次,我的索引为:1
我是重复的第3次,我的索引为:2
我是重复的第4次,我的索引为:3
我是重复的第5次,我的索引为:4

3.9、lazy()函数

关于Lazy()函数来说,它共实现了4个重载函数,都是用于延迟操作,不过这里不多做介绍。因为在实际的项目开发中常用都是用于延迟初始化属性。而关于这一个知识点我在前面的变量与常量已经讲解过了。这里不多做介绍…

四、对标准的高阶函数总结

关于重复使用同一个函数的情况一般都只有T.alsoT.letT.apply这三个函数。而这个三个函数在上面讲解这些函数的时候都用实例讲解了他们的区别。故而这里不做详细实例介绍。并且连贯着使用这些高阶函数去处理一定的逻辑,在实际项目中很少会这样做。一般都是单独使用一个,或者两个、三个连贯使用。但是在掌握了这些函数后,我相信您也是可以的。这里由于篇幅原因就不做实例讲解了…

关于他们之间的区别,以及他们用于实际项目中在一定的需求下到底该怎样去选择哪一个函数进行使用希望大家详细的看下他们的源码并且根据我前面所写的实例进行分析。

大家也可以参考这两篇文章:
掌握Kotlin标准函数:run, with, let, also and apply
那些年,我们看不懂的那些Kotlin标准函数

总结

既然我们选择了Kotlin这门编程语言。那其高阶函数时必须要掌握的一个知识点,因为,在系统的源码中,实现了大量的高阶函数操作,除了上面讲解到的标准高阶函数外,对于字符串(String)以及集合等,都用高阶函数去编写了他们的一些常用操作。比如,元素的过滤、排序、获取元素、分组等等
对于上面讲述到的标准高阶函数,大家一定要多用多实践,因为它们真的能在实际的项目开发中减少大量的代码编写量

本文已收录至GitHub: Jetictors/KotlinLearn,欢迎star

文章持续更新中,可以微信搜索「J佬杂谈 」第一时间阅读, 大家的三连击是对老J最好的动力了,你确定不来一波吗?

如果本篇博客有任何错误和建议,欢迎人才们留言

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

总结

Android架构学习进阶是一条漫长而艰苦的道路,不能靠一时激情,更不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

上面分享的字节跳动公司2020年的面试真题解析大全,笔者还把一线互联网企业主流面试技术要点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

就先写到这,码字不易,写的很片面不好之处敬请指出,如果觉得有参考价值的朋友也可以关注一下我

①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包阅读下载,最后觉得有帮助、有需要的朋友可以点个赞

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img
企业主流面试技术要点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

就先写到这,码字不易,写的很片面不好之处敬请指出,如果觉得有参考价值的朋友也可以关注一下我

①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包阅读下载,最后觉得有帮助、有需要的朋友可以点个赞

[外链图片转存中…(img-7TuHfwbM-1712799226978)]

[外链图片转存中…(img-AWmwa7mU-1712799226979)]

[外链图片转存中…(img-yoz71lKU-1712799226979)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-PEP3qwdc-1712799226979)]

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值