Kotlin基础 - 第十一章高阶函数
#### [kotlin官方文档 https://www.kotlincn.net/docs/reference/](https://www.kotlincn.net/docs/reference/) ####
高阶函数
以另一个函数作为参数或者返回值的函数被称为高阶函数。
高阶函数可以把函数作为参数传递或者返回值返回的函数。既然函数对象作为数值进行传递那么就会有如何引用函数的问题。函数引用的三种方式:
1. 直接双冒号的方式,引用的是包级别的函数;####
// 这种引用适用于lambda表达式只有一个函数调用并且
// 这个函数的参数也是这个lambda表达式的参数
args.forEach(::println)
2.类名双冒号函数名的方法引用的方法通常要包含自己的实例作为第一个参数,比如扩展方法函数;
函数没有构造方法或构造参数:
class MyInt {
fun showMsg() {
println("参数打印")
}
}
val display: (MyInt) -> Unit = MyInt::showMsg
fun main(args: Array<String>) {
//函数调用 ,注意如果没有无参数的方法,程序报错将编译不过去
display(MyInt())
//执行结果: 参数打印
}
fun String?.isEmpty(): Boolean = this == null || this == ""
val isEmpty: (String) -> Boolean = String::isEmpty
函数有构造方法并且需要构造参数
class MyInt(var tag: String?) {
fun showMsg(): String {
println(tag)
return "执行成功"
}
}
fun main(args: Array<String>) {
//这个可以理解为方法代理
val text: (MyInt) -> String = MyInt::showMsg
var msg = text(MyInt("执行标签"))
msg.println()
}
执行结果:
执行标签
执行成功
注意:这种方式的调用是有限制的,仅可以调用它的无惨方法,对于有参数的方法是无法这样调用的
3.实例双冒号和方法名,这是后第一个实例是调用实例对象
class MyLogger(val tag: String) {
fun print(i: Int) {
println("$tag $i")
}
}
fun main(args: Array<String>) {
val arr = intArrayOf(1, 2, 4, 6)
arr.forEach(MyLogger("TAG")::print)
}
自定义高阶函数
首先来看一个 String.forEach
函数的底层实现,核心代码就是 (action: (Char) -> Unit): Unit
,这个是所有高级函数定义的核心
/**
* Performs the given [action] on each character.
*/
public inline fun CharSequence.forEach(action: (Char) -> Unit): Unit {
for (element in this) action(element)
}
其实如果你看懂了这个代码
class MyInt {
fun showMsg() {
println("参数打印")
}
}
val display: (MyInt) -> Unit = MyInt::showMsg
其实你就可以明白
for (element in this) action(element)
其实本质就是 action()
代表个传入的这个函数代码块 :
后面括号里面=的是这个函数需要接收的参数
再看函数调用,以前我们常见的函数接收的参数都是基本数据类型的,无非在高阶函数中接收的是一个符合签名规则函数表达式可以理解为字符串式
那么调用就变成下面这样的
MyInt().test(::println)
如果这些你都看明白了,那我们就开始愉快的自定义高阶函数了
先来一个简单的例子
class MyInt {
var num: String = "default"
fun test(name: (String) -> Unit) {
//这里的this代表的就是类对象本身
name(this.num)
}
}
//函数调用
MyInt().test(::println)
扩展函数我们参考系统的 println
函数也自己实现一个
fun String.displayChars(name: (Char) -> Unit) {
println(this)
for (element in this) name(element)
}
//函数调用
val a = "zhoubencheng"
a.displayChars(::println)
Kotlin中内置的高阶函数
明白了上面的这些我们一起来学习一下kotlin本省为我们提供的高阶函数
forEach 枚举每一个元素
fun main(args: Array<String>) {
var valueStr = listOf("Hello", " Word", "!")
valueStr.forEach(::print)
}
打印结果
Hello Word!
map 函数变换(映射),列举每一个元素,将元素的类型由A转换成C然后拼接成一个新的集合
fun main(args: Array<String>) {
var valueStr = listOf("Hello", "Word", "!")
valueStr
.map { "$it " }
.forEach(::print)
}
打印结果
Hello Word !
如果是一个复杂的集合怎么转换呢
fun main(args: Array<String>) {
var valueStr = listOf("Hello", "Word", "!")
valueStr
.map { str ->
str.map { char ->
"$char"
}
}
.forEach(::print)
}
打印结果
[H, e, l, l, o][W, o, r, d][!]
源码
map 函数变换(映射),列举每一个元素,将元素的类型由A转换成C然后拼接成一个新的集合
fun main(args: Array<String>) {
var valueStr = listOf("Hello", "Word", "!")
valueStr
.map { "$it " }
.forEach(::print)
}
打印结果
Hello Word !
源码
最终转换成一个R类型返回
map 函数变换(映射),列举每一个元素,将元素的类型由A转换成C然后拼接成一个新的集合
fun main(args: Array<String>) {
var valueStr = listOf("Hello", "Word", "!")
valueStr
.flatMap { str ->
str.toList().map { "$it " }
}
.forEach(::print)
}
打印结果
H e l l o W o r d !
可以看到,与map不一样的是flatMap的作为参数的函数[transform]返回的不是单纯的数据R而是一个R的集合[Iterable],而flatMap本身的返回值同map一样是一个List
源码
最终转换成一个List类型返回
reduce 无初始值的函数累加操作,不仅局限在数值类型,也可以是字符串等类型
fun main(args: Array<String>) {
var valueStr = listOf("Hello", "Word", "!")
valueStr.reduce { acc, s -> "$acc $s " }.println()
var valueNum = listOf(1, 4, 5, 6)
valueNum.reduce { acc, i -> acc + i }.println()
valueNum.reduce { acc, i ->
print("$i + ")
acc + i * 2
}.println()
}
打印结果
Hello Word !
16
4 + 5 + 6 + 31
其中 acc 是积累值,可以为任何值
实现一个求阶乘的函数式
/**
* 求1到num的阶乘
*/
fun factorial(num: Long): Long {
if (num <= 0L) return 1
return (1..num).reduce { acc, l -> acc * l }
}
fun main(args: Array<String>) {
(0L..6L).map(::factorial).forEach(::println)
}
运行结果
1
1
2
6
24
120
720
源码
fold 带有始值的函数累加操作,不仅局限在数值类型,也可以是字符串等类型
fun main(args: Array<String>) {
var valueStr = listOf("Hello", "Word", "!")
valueStr.fold("输入迭代的初始值 ") { acc, str -> "$acc $str" }.forEach(::print)
}
运行结果
输入迭代的初始值 Hello Word !
使用fold轻松完成字符串拼接
fun main(args: Array<String>) {
var valueStr = listOf("Hello", "Word", "!")
valueStr.fold(StringBuilder()) { acc, str -> acc.append(str).append(",") }.forEach(::print)
}
运行结果
Hello,Word,!,
源码
foldIndexed(效果和fold一样,增加了索引) 带有始值的函数累加操作,不仅局限在数值类型,也可以是字符串等类型
fun main(args: Array<String>) {
var valueStr = listOf("Hello", "Word", "!")
valueStr.foldIndexed(StringBuilder()) { index, acc, str -> acc.append(index).append(str).append(",") }.forEach(::print)
}
运行结果
0Hello,1Word,2!,
源码
foldRight(效果和fold一样,只不过将元素的顺序倒叙遍历) 带有始值的函数累加操作,不仅局限在数值类型,也可以是字符串等类型
fun main(args: Array<String>) {
var valueStr = listOf("Hello", "Word", "!")
valueStr.foldRight(StringBuilder()) { str, acc -> acc.append(str).append(",") }.forEach(::print)
}
运行结果
!,Word,Hello,
源码
foldRightIndexed(效果和foldRight一样,只不过多了一个Index参数) 带有始值的函数累加操作,不仅局限在数值类型,也可以是字符串等类型
fun main(args: Array<String>) {
var valueStr = listOf("Hello", "Word", "!")
valueStr.foldRightIndexed(StringBuilder()) { index, str, acc -> acc.append(str).append(",") }
.forEach(::print)
}
运行结果
!,Word,Hello,
源码
joinToString 字符串连接函数
fun main(args: Array<String>) {
var valueStr = listOf("Hello", "Word", "!")
valueStr.joinToString(separator = ",", prefix = "《", postfix = "》"){
"$it _"
}.println()
}
运行结果
《Hello _,Word _,! _》
源码
filter 函数过滤表达式
fun main(args: Array<String>) {
var valueStr = listOf("Hello", "Word", "!")
valueStr.filter { "Word" == it }.forEach(::print)
}
运行结果
Word
源码
filterIndexed 带索引的函数过滤表达式
fun main(args: Array<String>) {
var valueStr = listOf("Hello", "Word", "!")
valueStr.filterIndexed { index, s -> index == 1 }.forEach(::print)
}
运行结果
Word
源码
takeWhile 从集合中取元素,直到遇到第一个不符合条件的元素停止
fun main(args: Array<String>) {
var valueStr = listOf("Hello", "Word", "!")
valueStr.takeWhile { s -> "Word" != s }.forEach(::print)
}
运行结果
Hello
如果真则停止取值
源码
let 使用代码块快速指代调用者包含调用者状态
使用之前我们的代码是这样的
data class PersonMan(var name: String, var age: Int)
fun findPerson(): PersonMan? {
return PersonMan("xiaoming", 24)
}
fun main(args: Array<String>) {
var person = findPerson()
//每次使用都需要判空
println(person?. name)
println(person?. age)
}
运行结果
xiaoming
24
使用之后我们的代码是这样的
data class PersonMan(var name: String, var age: Int)
fun findPerson(): PersonMan? {
return PersonMan("xiaoming", 24)
}
fun main(args: Array<String>) {
findPerson().let { personMan ->
//按照函数特性 personMan=findPerson()
println(personMan?. name)
println(personMan?. age)
}
}
运行结果
xiaoming
24
代码我们再进一步优化一下
data class PersonMan(var name: String, var age: Int)
fun findPerson(): PersonMan? {
return PersonMan("xiaoming", 24)
}
fun main(args: Array<String>) {
findPerson()?.let { personMan ->
//按照函数特性 personMan=findPerson()?
println(personMan. name)
println(personMan. age)
}
}
}
运行结果
xiaoming
24
使用let函数我们可以很好的简化空判断,简化我们的代码逻辑。简直就是编码神奇啊
源码
apply 使用代码块直接调用代码块的方法或者属性
data class PersonMan(var name: String, var age: Int) {
fun work(workName: String) {
println("$name $age 岁,是一个: $workName")
}
}
fun findPerson(): PersonMan? {
return PersonMan("xiaoming", 24)
}
fun main(args: Array<String>) {
findPerson()?.apply {
work("律师")
println(name)
println(age)
}
}
运行结果
xiaoming 24 岁,是一个: 律师
xiaoming
24
使用
apply
函数我们可以很好的简化空判断并且简化我们的函数调用的复杂度
源码
with
函数,使用代码块直接调用代码块的方法或者属性,区别于 apply
函数的是调用者参数是直接传进来的
fun main(args: Array<String>) {
var reader = BufferedReader(FileReader("G:\\HightLevelDemo.kt"))
with(reader) {
var lines: String?
while (true) {
lines = readLine() ?: break
println(lines)
}
close()
}
}
运行结果
package zbc.com.calclib
import java.io.BufferedReader
import java.io.FileReader
fun main(args: Array<String>) {
var reader = BufferedReader(FileReader("G:\\HightLevelDemo.kt"))
with(reader) {
var lines: String?
while (true) {
lines = readLine() ?: break
println(lines)
}
close()
}
}
源码
kotlin提供的读取小文件的代码
-
BufferedReader(FileReader("\\path"))readLines()
函数,读取小文件 -
BufferedReader(FileReader("\\path")).readText().println()
函数,读取小文件 -
use函数
使用普通函数读取文件还得自己关闭流文件太麻烦,其实可以使用 use
函数,函数内部帮助我们维护函数流的关闭
fun main(args: Array<String>) {
BufferedReader(FileReader("G:\\MKotlinApp\\CalcLib\\src\\main\\kotlin\\zbc\\com\\calclib\\HightLevelDemo.kt")).use {
var lines: String?
while (true) {
lines = it.readLine() ?: break
println(lines)
}
}
}
源码