1.高阶函数
基本概念: 传入或者返回函数的函数
函数引用:引用的函数名前加上 ::
- 有以下几种类型:
- 类成员方法引用:
类名::成员方法名
- 扩展函数引用:
类名::扩展函数名
- 实例函数引用:
实例名::成员方法名
- 包级别函数引用:
::函数名
第一个例子:
打印数组中的元素(传入包级别函数)
forEach(action: (T) -> Unit)
:要求传入了一个函数,参数为(action: (T) -> Unit)
,类型为一个参数T
,返回值为Unit
println(message: Any?)
:类型为一个参数T
,返回值为Unit
我们调用args.forEach(::println)
将println
函数传入给forEach
第二个例子:
过滤数组中的空字符串(传入类成员函数)
这里有个点要注意下: filter
要求传入的函数类型为(predicate: (T) -> Boolean)
,但是我们传入的String::isNotEmpty
这个方法并没有参数!!!public inline fun CharSequence.isNotEmpty(): Boolean = length > 0
只有一个返回值Boolean
为什么可以呢???
答案:因为
类名::成员方法名
默认就有一个参数,这个函数类型就是类名这个类型的
。比如上面的String::isNotEmpty
相当于isNotEmpty(String)
第三个例子:
打印数组中的元素(传入实例函数)
这里传入的是t::testName
,实例名::成员方法名
就不会默认多出一个参数。如果使用Test::testName
会显示报错信息,也验证了我们上面说的类名::成员方法名
默认就有一个参数。
这里总结一下:函数引用,就是将函数作为参数变量传入具体某个方法中,也可以赋值给变量。注意的是,如果是类成员函数、扩展函数引用(
类名:函数名
),默认参数会多一个就是类本身这个参数
2. 闭包
- 函数运行的环境
- 持有函数运行状态
- 函数内部可以定义函数/类
函数的定义方法可以传入函数,也可以返回函数,函数内的作用域包含了函数内的子函数跟子类等。 格式 :
fun 方法名(形参:函数类型) 函数类型{}
函数类型基本写法:() -> Unit
(多个参数) -> 返回类型
3. 函数复合
- f(g(x)) 函数传入函数
上面是基本的展示,函数中传入函数。 下面扩展一下函数:
上面实际上就是扩展函数,然后在函数中传入函数跟返回函数,只有一个参数的则使用了
infix
中缀关键字。关键点1、2、3都是扩展了函数类型,其中关键点1跟2 扩展函数类型为传入一个函数参数,关键点3扩展函数传入两个函数参数
举例:关键点1:函数类型为
Function<P1,P2>
扩展函数andThen
,传入函数类型Function1<P2, R>
,返回函数类型Function1<P1, R>
第一个return
:返回函数类型为fun(p1: P1): R
第二个return
:function.invoke(this.invoke(p1))
,先是传入this.invoke(p1)
再将这里返回的值传入function.invoke()
.
先调用了了andThen
前的函数,再调用andThen
后面的函数。
大家可以根据这些写法自定义多种扩展函数~~
4. run、let、with、apply、also等语法糖部分解析
- 先以
run
为例子
方法一函数签名:run(block: () -> R): R
直接传入代码块,并返回R
方法二函数签名:T.run(block: T.() -> R): R
,传入的代码块block: T.() -> R
,也就是调用者本身的引用,则在block
中则直接可以使用T
中的成员变量及函数等
这个
contract {...}
看不懂可以暂时不用管它,kotlin
中契约的一种写法,详情可以看一下kotlinlang.org/docs/refere…
with
、apply
、also
、let
with
:接受两个参数,一个是自己本身,一个block: T.() -> R
,返回return receiver.block()
;
with
用法:
apply
:方法的扩展函数,传入block: T.() -> Unit
,返回调用者本身,用法与run
一致,但是最后返回的是调用者本身。also
:方法的扩展函数,传入block: (T) -> Unit
,这里更前面几个方法有点不一样,block
传入了T
这个调用者本身,并且函数最后返回调用者本身。also
用法:
let
:方法的扩张函数,传入block: (T) -> R
,let
方法返回R
。let
用法:
补充: 尾递归优化 tailrec
- 将
tailrec
关键字添加到fun
前提示编译器尾递归优化。 尾递归:是递归的一种形式,递归中在调用完自己后没有其他操作的称为尾递归。 - 尾递归与迭代的关系:尾递归可以直接转换成迭代(好吧,其实这个我也不是很清楚~)
上面的方法中第一种是符合尾递归的形式,这种我们可以加tailrec
关键字,有什么好处呢?
因为加了tailrec
关键字,实际上是优化成了迭代相比递归减低了内存空间的开销。