6章、kotlin函数进阶

139 阅读4分钟

6.1、⾼阶函数

定义

参数包含函数类型或者返回值为函数类型。

例⼦

// 参数param是函数类型
fun test1(param: () -> Int) {
    // 涉及函数的调⽤
    // param()
    param.invoke()
}
// 返回值是函数类型
fun test2(): () -> Int {
    // 返回的是⼀个匿名函数,返回值是1
    return { 1 }
}



// 定义⼀个数组
var arr = intArrayOf(1, 2, 3, 4)
// 遍历功能
arr.forEach {
    Log.e(TAG, it.toString())
}
Log.e(TAG, arr.joinToString())
// 遍历,然后转成另外的元素,然后存到数组中
val map = arr.map {
    it * it
}
Log.e(TAG, map.joinToString())

⾼阶函数的调⽤

// 定义⼀个数组
var arr = intArrayOf(1, 2, 3, 4)
// ⾼阶函数的引⽤ ::println表⽰对函数的引⽤
arr.forEach(::println)

⾼阶函数调⽤-简化的⼏个阶段

⾮常关键

// 定义⼀个数组
var arr = intArrayOf(1, 2, 3, 4)
// 第⼀种⽅式
arr.forEach({
println("Hello $it")
})
// 如果接受的⾼阶函数是最后⼀个参数,⼩括号可以移动到外⾯
arr.forEach() {
println("Hello $it")
}
// 如果括号中没有任何的参数,那么可以将⼩括号省略
arr.forEach {
println("Hello $it")
}

⾼阶函数应⽤ - 计算⽅法的时间

// ⾸先传⼊⼀个匿名函数
// const({
// (1..10000000).forEach { it * it }
// })

// 优化成下⾯的样式
// const(){
// (1..10000000).forEach { it * it }
// }

// 继续优化
const {
(1..10000000).forEach { it * it }
}

6.2、内联函数

定义

减少了函数的调⽤,性能优化。

具体看下⾯的分析:

我们可以看到Array.kt⽂件中forEach的定义,⾥⾯有⼀个关键字inline,它有什么作⽤呢?

public inline fun IntArray.forEach(action: (Int) -> Unit): Unit {
    for (element in this) action(element)
}

我们在代码中调⽤格式如下:

// 定义⼀个数组
var arr = intArrayOf(1, 2, 3, 4)
// 第⼀种⽅式
arr.forEach({
    println("Hello $it")
})

编译器最终转换为的是⼀个内联函数。

// 定义⼀个数组
var arr = intArrayOf(1, 2, 3, 4)
// 第⼀种⽅式
for(element in arr){
    println(element.toString())
}

内联函数的优点

减少了函数的调⽤,性能更好。

⾼阶函数与内联函数更加匹配

⽐如下⾯的函数

class MainActivity2 : AppCompatActivity() {
    val TAG = "cdx"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        const({
            print("1")
        })
    }

    fun const(block: () -> Unit) {
        var start = System.currentTimeMillis()
        block()
        var end = System.currentTimeMillis()
        Log.e(TAG, (end - start).toString())
    }
}

然后我们查看下编译后的代码(输⼊Show Kotlin Bytecode)

我们看下其中2个消耗时间的地⽅ 

1、调⽤了const⽅法。 

2、创建了⼀个匿名函数。 

然后我们优化const⽅法的定义,增加inline关键字,让他变成⼀个内联函数。

inline fun const(block: () -> Unit) {
    var start = System.currentTimeMillis()
    block()
    var end = System.currentTimeMillis()
    Log.e(TAG, (end - start).toString())
}

然后我们查看下编译后的代码(输⼊Show Kotlin Bytecode)

我们可以看到,省去了函数的调⽤,性能更⾼。

内联函数return(类似于continue)

这个地⽅增加return,导致forEach下⾯的语句都不执⾏

// 定义⼀个数组
var arr = intArrayOf(1, 2, 3, 4)
// 第⼀种⽅式
arr.forEach {
// 这个地⽅相当于是break
    if (it == 3) return
    Log.e(TAG, it.toString())
}

这个地⽅类似于continue

// 定义⼀个数组
var arr = intArrayOf(1, 2, 3, 4)
arr.forEach {
// 这个地⽅相当于是continue
    if (it == 3) return@forEach
    Log.e(TAG, it.toString())
}

内联属性

属性的getter和setter可以被内联,只需要在getter和setter上增加inline

class MainActivity2 : AppCompatActivity() {
    val TAG = "cdx"
    var pocket: Double = 0.0
    // 内联属性
    var money: Double
        inline get() = pocket
        inline set(value) {
            pocket = value
        }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        money = 5.0
    }
}

然后我们编译下

发现money属性已经没有了,money属性已经被替换了。

6.3、⼏个有⽤的⾼阶函数

let、run

let:当我们需要定义⼀个变量在特定的作⽤域内,let函数是⼀个不错的选择。

// 定义对象
var person = Person("张三", 25)
val name = person.let {
    it.name
}
Log.e(TAG, name.toString())

run:和let的作⽤相似,只是⼀个是it,⼀个是this

// 定义对象
var person = Person("张三", 25)
var result = person.run {
    this.age
}
Log.e(TAG, result.toString())

also、apply

// 定义对象
var person = Person("张三", 25)
// 利⽤also来修改对象属性
var person2 = person.also {
    it.name = it.name + "V2"
}
Log.e(TAG, person2.toString())
var person3 = person.apply {
    name = "xxx"
}
Log.e(TAG, person3.toString())

6.4、集合变换与序列

filter操作

筛选出来合适的,然后放到集合中

val list = intArrayOf(1, 2, 3, 4)
// 取出偶数的值,过滤
val result = list.filter { it % 2 == 0 }
Log.e(TAG, result.toString()) // [2, 4]

map操作

遍历数组,然后进⾏操作,然后放到集合中

val list = intArrayOf(1, 2, 3, 4)
val result = list.map { it * 2 }
Log.e(TAG, result.joinToString())

flatMap操作

flatMap操作的流程图 

1)⾸先取出来元素。 

2)然后根据元素⽣成数组。

3)然后在将这些数组进⾏拼接。

val list = intArrayOf(1, 2, 3, 4)
val result = list.flatMap { 0 until it }
Log.e(TAG, result.joinToString()) // 0, 0, 1, 0, 1, 2, 0, 1, 2, 3

fold(折叠)操作

有⼀个初始化的值 + 拼接的规则

val list = intArrayOf(1, 4, 7)
// 初始化的值为StringBuilder()
// acc为拼接的值
list.fold(StringBuilder()) { acc, i ->
    Log.e(TAG, "acc=$acc")
    Log.e(TAG, i.toString())
    acc.append(i)
}

懒序列懒在哪⾥?

通过调⽤asSequence(),然后只有调⽤forEach⽅法(fold、ruduce、sum)以后, 才会去执⾏上⾯的filter、map、flatMap⽅法。

6.5、SAM(单⼀抽象⽅法转换)转换

SAM转换例⼦

如果只有⼀个⽅法,那么这个匿名内部类可以⾮常的简化,只有⼀个{ } 。

// SAM例⼦
var exe: ExecutorService? = null
// 匿名内部类
// exe?.submit(object : Runnable {
//  override fun run() {
//      Log.e(TAG,"任务执⾏的")
// }
// })

// 简化写法
// exe?.submit({
//      Log.e(TAG, "任务执⾏的")
// })

// 继续简化
exe?.submit {
    Log.e(TAG, "任务执⾏的")
}

kotlin的匿名内部类

// 匿名内部类的通常写法
object : Runnable {
    override fun run() {
        Log.e(TAG,"任务执⾏的")
    }
}
// 匿名内部类的简写写法
Runnable {
    Log.e(TAG,"任务执⾏的")
}