函数的参数接收的是另一个函数,或者返回值是另一个函数类型,我们把这类函数称为高阶函数
字符串的类型用String表示,整型用Int表示,那么函数的类型呢?
// 参数block接收的是函数类型,该函数具体是无参,返回值为Unit的函数
fun start(block: () -> Unit) {
}
// 参数block接收的是函数类型,该函数具体是有一个String类型参数,返回值为Boolean的函数
fun start(block: (String) -> Boolean) {
}
定义一个高阶函数
// function函数,参数需要两个Int,返回值也是Int的类型
fun number(num1: Int, num2: Int, function: (Int, Int) -> Int): Int {
return function(num1, num2)
}
通过Lambda的形式调用高阶函数(较为常用)
// Lambda最后一行代码的返回值作为函数的返回值返回
val result1 = number(5, 10) { num1, num2 ->
num1 + num2
}
val result2 = number(10, 8) { num1, num2 ->
num1 * num2
}
// 顶层函数引用,forEach的参数函数类型是(T) -> Unit,其中T表示的泛型就是list存储的类型String
// 而函数print的参数是any,返回是Unit,符合forEach需要的函数类型,因此通过::形式表示引用print函数
val list = arrayListOf<String>()
list.forEach(::print)
// 引用同一个类中的test函数
fun main(args: ArrayList<String>) {
val list = arrayListOf<String>()
list.forEach(::test)
}
private fun test(content: String) {
println(content)
}
// 扩展函数引用
// filter的参数函数类型是:(T) -> Boolean,而String的扩展函数isNotEmpty的参数是(),返回值是Boolean。
// 看着类型都匹配不上,为什么没问题?由于T是String,所以filter实际上的需要的函数类型是(String) -> Boolean
// 而扩展函数有一个隐藏参数,就是调用者。因此isNotEmpty实际的参数是(String),返回值是Boolean,因此两者是对得上的。
val list = arrayListOf<String>()
list.filter(String::isNotEmpty)
// 其他类里的函数引用,和扩展函数的引用一样,通过类名::函数名,并且同样也有一个隐藏参数,就是调用者,即Animal对象
class Animal {
fun isDog(): Boolean {
return true
}
}
fun main(args: ArrayList<String>) {
val list = arrayListOf<Animal >()
list.filter(Animal::isDolg)
}
// 类实例的函数引用,这种情况下,因为是实例化,所以就没有隐藏参数一说
class Animal {
fun isCat(name: String): Boolean {
return name == "Cat"
}
}
fun main(args: ArrayList<String>) {
val list = arrayListOf<String>()
// filter需要的函数类型是(String) -> Boolean
val animal = Animal ()
list.filter(animal::isCat)
}
/**
- map,对元素遍历处理和转换
*/
fun main() {
val stringList = arrayListOf<String>()
val intList = stringList.map {
it.toInt()
}
val newList = intList.map {
it * 5 + 10
}
}
fun main() {
val list = arrayListOf<ArrayList<Int>>()
// newList是List类型
val newList = list.flatMap { innerList ->
innerList.map { element ->
element.toString()
}
}
// newList2是List类型
val newList2 = list.flatMap {
it
}
}
// 筛选出符合条件的元素,添加到一个新的数组
val array = intArrayOf(1, 2, 8, 11, 22, 55, 66)
val newArrayList = array.filter {
it % 2 == 0
}
// result=true,any表示是否有其中一个满足Lambda表达式,即int数组中是否有一项it%5==0
val array = intArrayOf(1, 2, 8, 11, 22, 55, 66)
val result = array.any {
it % 5 == 0
}
// result=false,all表示是否所有项都满足Lambda表达式
val array = intArrayOf(1, 2, 8, 11, 22, 55, 66)
val result = array.all {
it % 5 == 0
}
// result=ture,none表示是否所有项都不满足Lambda表达式
val array = intArrayOf(1, 2, 8, 11, 22, 55, 66)
val result = array.none {
it % 9 == 0
}
// result=[1],遍历找出符合条件,和filter有点像,但是filter不同地方在filter总是会遍历当前IntArray的所有元素,
// 而takeWhile在第一次发现predict不满足的时候就不再遍历,后面的元素即使满足条件也不会加入到结果中。
val array = intArrayOf(1, 2, 8, 11, 22, 55, 66)
val result = array.takeWhile {
it % 2 != 0
}
println(result)
两个函数都是对集合的遍历,遍历完成之后能得到一个结果。
reduce的返回值类型必须和集合的元素类型相符。
fold的返回值类型则不受约束,并且有一个初始值。
fun main(args: Array<String>) {
val list = listOf(1, 2, 3, 4, 5)
// 1到5求和,acc是累加的返回值,i是当前遍历列表中的值
println(list.reduce { acc, i -> acc + i })
}
// 求阶乘
fun factorial(n: Int): Int {
if (n == 0) return 1
return (1..n).reduce { acc, i -> acc * i }
}
// result=1,2,8,11,
val array = intArrayOf(1, 2, 8, 11)
val result = array.fold(StringBuilder()) { sb, i ->
sb.append(i)
sb.append(",")
}
println(result)
var list: ArrayList<Int>? = ArrayList()
@Test
fun test() {
// this指向的就是list,并且返回的oldList还是原来的list
val oldList = list?.apply {
this.add(5)
this.add(10)
this.add(20)
}
println(oldList.toString())
// it指向的是list,Lambda表达式的最后一行表示返回值
val addResult = list?.let {
it.add(30)
it.add(50)
true
}
println(addResult)
// 相当于apply和let的组合体,this指向的是list,Lambda表达式的最后一行表示返回值
val addResultSize = list?.run {
this.add(60)
this.size
}
println(addResultSize)
// 和run函数一样,区别是run是扩展函数,而with是普通函数,调用方式不同
val addResultSize2 = with(list!!) {
this.add(80)
this.size
}
println(addResultSize2)
}
use函数会自动关闭调用者(无论中间是否出现异常),Kotlin的File对象和IO流操作变得行云流水
use函数内部实现也是通过try-catch-finally块捕捉的方式,所以不用担心会有异常抛出导致程序退出
close操作在finally里面执行,所以无论是正常结束还是出现异常,都能正确关闭调用者
BufferedReader(FileReader("build.gradle")).use {
var readLine: String?
while (true) {
readLine = readLine() ?: break
println(readLine)
}
}
尾递归:函数的末尾递归调用函数自己
@Test
fun test() {
// 5+4+3+2+1
println(add(5))
// 会抛出StackOverflowError异常
println(add(100000))
}
private fun add(num: Int): Int {
if (num == 1) {
return 1
}
return num + add(num - 1)
}
要理解这个问题,先回顾下方法的执行过程。一个应用程序的执行实际上可以看做是一个个方法入栈出栈的过程。当调用一个方法的时候,会将该方法入栈,当方法执行完毕后,就会执行出栈操作。这个栈可以被称为方法栈,方法栈是有长度限制的(实际上栈也是一块内存区域),当我们入栈的方法长度超过了方法栈的最大限制就会抛出StackOverflowError异常。
那为什么我们平时很少碰到这个异常?这是因为,正常的方法调用都会在该方法执行完成后被清理出栈,因此栈长度一般都会在最大的长度范围之内。
那么尾递归怎么优化呢?关键字tailrec
tailrec修饰的函数,其未递归返回的需要是一个函数整体add(num - 1, count + num),而不能是num + add(num - 1)。
另外,try catch finally异常块里,不能使用tailrec关键字修饰函数
@Test
fun test() {
// 5+4+3+2+1
println(add(100000, 0))
}
private tailrec fun add(num: Int, count: Long): Long {
if (num == 0) {
return count
}
return add(num - 1, count + num)
}
函数里面声明函数,函数里面返回函数,就是闭包
闭包是能够读取其他函数内部变量的函数
@Test
fun test() {
// 变量f指向count函数里的匿名函数
val f = count()
// 多次调用该匿名函数
f()
f()
}
private fun count(): () -> Unit {
var conut = 0
// 返回一个匿名函数,这个函数持有count的状态
return fun() {
println(++conut)
}
}