- 一、什么是高阶函数
- 1. 将函数用作函数参数的情况的高阶函数
- 2. 将函数用作一个函数的返回值的高阶函数
- 3. 高阶函数的使用
- 二、自定义高阶函数
- 三、常用高阶函数
- 1. TODO函数
- 2. run 函数
- 3. with()函数
- 4. T.apply()函数
- 5. T.also()函数
- 6. T.let()函数
- 7. T.takeIf()函数
- 8. T.takeUnless()函数
- 9. repeat()函数
- 10. lazy()函数
在 Kotlin 中,高阶函数即指:将函数用作一个函数的参数或者返回值的函数。
1. 将函数用作函数参数的情况的高阶函数sumBy{}
高阶函数源码:
// sumBy函数的源码
public inline fun CharSequence.sumBy(selector: (Char) -> Int): Int {
var sum: Int = 0
for (element in this) {
sum += selector(element)
}
return sum
}
解释说明:
- 该函数返回一个
Int
类型的值。并且接受了一个selector()
函数作为该函数的参数。其中,selector()
函数接受一个Char
类型的参数,并且返回一个Int类型的值。 - 定义一个
sum
变量,并且循环这个字符串,循环一次调用一次selector()
函数并加上sum
。用作累加。其中this
关键字代表字符串本身。
这个函数的作用是:把字符串中的每一个字符转换为 Int 的值,用于累加,最后返回累加的值。
例子:
val testStr = "abc"
val sum = testStr.sumBy { it.toInt() }
println(sum)
输出结果:
294 // 因为字符a对应的值为97,b对应98,c对应99,故而该值即为 97 + 98 + 99 = 294
2. 将函数用作一个函数的返回值的高阶函数lock() 函数源码:
fun lock(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}
解释说明:
- 从源码可以看出,该函数接受一个 Lock 类型的变量作为参数 1,并且接受一个无参且返回类型为 T 的函数作为参数 2.
- 该函数的返回值为一个函数,我们可以看这一句代码
return body()
可以看出。
例子:
fun toBeSynchronized() = sharedResource.operation()
val result = lock(lock, ::toBeSynchronized)
3. 高阶函数的使用
这里主要讲高阶函数中对 Lambda 语法的简写:
str.sumBy( { it.toInt } )
根据 Kotlin 中的约定,即当函数中只有一个函数作为参数,并且您使用了 lambda 表达式作为相应的参数,则可以省略函数的小括号()。
str.sumBy{ it.toInt }
当函数的最后一个参数是一个函数,并且你传递一个lambda表达式作为相应的参数,则可以在圆括号之外指定它。
val result = lock(lock){
sharedResource.operation()
}
二、自定义高阶函数
例子:传入两个参数,并传入一个函数来实现加减乘除。
private fun resultByOpt(num1 : Int , num2 : Int , result : (Int ,Int) -> Int) : Int{
return result(num1,num2)
}
fun main() {
val result1 = resultByOpt(1,2){
num1, num2 -> num1 + num2
}
val result2 = resultByOpt(3,4){
num1, num2 -> num1 - num2
}
val result3 = resultByOpt(5,6){
num1, num2 -> num1 * num2
}
val result4 = resultByOpt(6,3){
num1, num2 -> num1 / num2
}
println("result1 = $result1")
println("result2 = $result2")
println("result3 = $result3")
println("result4 = $result4")
}
输出结果:
result1 = 3 result2 = -1 result3 = 30 result4 = 2
三、常用高阶函数 1. TODO函数这个函数不是一个高阶函数,它只是一个抛出异常以及测试错误的一个普通函数。
作用:显示抛出 NotImplementedError
错误。NotImplementedError
错误类继承至 Java 中的 Error
。
例子:
fun main(args: Array) {
TODO("测试TODO函数,是否显示抛出错误")
}
输出结果: 如果调用 TODO() 时,不传参数的,则会输出
An operation is not implemented
.
run 函数这里分为两种情况讲解,采用不同的 run 函数会有不同的效果。
- run() 当我们需要执行一个代码块的时候就可以用到这个函数,并且这个代码块是独立的。即我可以在 run() 函数中写一些和项目无关的代码,因为它不会影响项目的正常运行。
例子:
private fun testRun1() {
val str = "kotlin"
run{
val str = "java" // 和上面的变量不会冲突
println("str = $str")
}
println("str = $str")
}
输出结果:
str = java str = kotlin
- T.run() 当我们传入的 lambda 表达式想要使用当前对象的上下文的时候,我们可以使用这个函数。
例子:
val str = "kotlin"
str.run {
println( "length = ${this.length}" )
println( "first = ${first()}")
println( "last = ${last()}" )
}
输出结果:
length = 6 first = k last = n
3. with()函数with() 函数和 T.run() 函数的作用是相同的,这两个函数的区别在于:
- with 是正常的高阶函数,T.run() 是扩展的高阶函数。
- with函数的返回值指定了 receiver 为接收者。
例子:
val str = "kotlin"
with(str){
println( "length = ${this.length}" )
println( "first = ${first()}")
println( "last = ${last()}" )
}
输出结果:
length = 6 first = k last = n
4. T.apply()函数从 T.apply() 源码中在结合前面提到的 T.run() 函数的源码我们可以得出,这两个函数的逻辑差不多,唯一的区别是T,apply 执行完了 block() 函数后,返回了自身对象。
例子:为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)
}
5. T.also()函数
T.also 函数中的参数 block 函数传入了自身对象。故而这个函数的作用是用 block 函数调用自身对象,最后在返回自身对象。
例子:
"kotlin".also {
println("结果:${it.plus("-java")}")
}.also {
println("结果:${it.plus("-php")}")
}
"kotlin".apply {
println("结果:${this.plus("-java")}")
}.apply {
println("结果:${this.plus("-php")}")
}
输出结果:
结果:kotlin-java 结果:kotlin-php 结果:kotlin-java 结果:kotlin-php
6. T.let()函数T.let 的作用也不仅仅在使用空安全这一个点上。用 T.let 也可实现其他操作
例子:
"kotlin".let {
println("原字符串:$it") // kotlin
it.reversed()
}.let {
println("反转字符串后的值:$it") // niltok
it.plus("-java")
}.let {
println("新的字符串:$it") // niltok-java
}
"kotlin".also {
println("原字符串:$it") // kotlin
it.reversed()
}.also {
println("反转字符串后的值:$it") // kotlin
it.plus("-java")
}.also {
println("新的字符串:$it") // kotlin
}
"kotlin".apply {
println("原字符串:$this") // kotlin
this.reversed()
}.apply {
println("反转字符串后的值:$this") // kotlin
this.plus("-java")
}.apply {
println("新的字符串:$this") // kotlin
}
输出结果:
原字符串:kotlin 反转字符串后的值:niltok 新的字符串:niltok-java 原字符串:kotlin 反转字符串后的值:kotlin 新的字符串:kotlin 原字符串:kotlin 反转字符串后的值:kotlin 新的字符串:kotlin
7. T.takeIf()函数这个函数的作用是:传入一个你希望的一个条件,如果对象符合你的条件则返回自身,反之,则返回null
例子:
val str = "kotlin"
val result = str.takeIf {
it.startsWith("ko")
}
println("result = $result")
输出结果:
result = kotlin
8. T.takeUnless()函数这个函数的作用和T.takeIf()函数的作用是一样的。只是和其的逻辑是相反的。
例子:
val str = "kotlin"
val result = str.takeUnless {
it.startsWith("ko")
}
println("result = $result")
输出结果:
result = null
9. repeat()函数这个函数的作用是:根据传入的重复次数去重复执行一个我们想要的动作(函数)
例子:
repeat(5){ println("我是重复的第${it + 1}次,我的索引为:$it") }
输出结果:
我是重复的第1次,我的索引为:0 我是重复的第2次,我的索引为:1 我是重复的第3次,我的索引为:2 我是重复的第4次,我的索引为:3 我是重复的第5次,我的索引为:4
10. lazy()函数关于Lazy()函数来说,它共实现了4个重载函数,都是用于延迟操作,不过这里不多做介绍。