Kotlin基础 - 第十一章高阶函数

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)
	        }
	    }
	}

源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值