高阶函数
如果一个函数接收另一个函数作为参数,或者返回值的类型是另一个函数,那么称该函数为高阶函数。
fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
val result = operation(num1, num2)
return result
}
fun plus(num1: Int, num2: Int): Int {
return num1 + num2
}
fun minus(num1: Int, num2: Int): Int {
return num1 - num2
}
num1AndNum2
是高阶函数,前两个参数是两个整型的数字,第三个参数是一个能接受两个整型数字并返回一个整型数据的函数,在函数内部,操作就是将前两个参数传给了第三个参数即operation
函数。
之后又定义了两个函数,与operation
相匹配。
使用如下:
val a = 100
val b = 20
val result1 = num1AndNum2(a, b, ::plus)
val result2 = num1AndNum2(a, b, ::minus)
println("a+b=$result1")//120
println("a-b=$result2")//80
这里的高阶函数第三个参数使用的是函数引用的方式,表示将plus()
minus()
两个函数作为参数传递进入高阶函数。
kotlin支持Lambda表达式来完成高阶函数的调用。
以上功能就可以修改为:
val a = 100
val b = 20
val result1 = num1AndNum2(a, b){n1,n2->
n1+n2
}
val result2 = num1AndNum2(a, b){n1,n2->
n1-n2
}
println("a+b=$result1")
println("a-b=$result2")
这样就不需要额外定义与operation
对应的函数了,直接在lambda表达式中写函数实现。
用高阶函数实现StringBuilder拼接功能
fun StringBuilder.build(block:StringBuilder.()->Unit):StringBuilder{
block()
return this
}
给StringBuilder
设置了一个build的扩展函数,这个扩展函数接收一个无返回值的block函数,整个高阶函数返回一个StringBuilder
在block函数的参数类型前加calssName.
意思是规定了这个函数定义所在的类。方便我们调用build的时候传入的lambda表达式直接拥有StringBuilder的上下文。
内联函数
其实我们一直使用的lambda表达式,在底层被转换成了匿名类的实现方式。表明我们每调用一次;lambda表达式都会造成额外的性能和内存开销。为了解决这个问题,就提出使用内联函数,只需要在定义高阶函数时,在前面加上inline
关键字。
使用内联函数时,kotlin编译器会将内联函数的代码在编译的时候自动替换到调用它的地方,保证不出现额外的开销。
但当我们接收两个或两个以上的函数类型的参数时,我们将高阶函数声明为内联,此时我们的需求是只与指定的函数参数内联,此时需要noinline
关键字。
inline fun inlineTest(block1:()->Unit,noinline block2:()->Unit){
//此时只有block1被内联
}
内联与非内联的区别:
-
内联的函数参数在编译的时候会被进行代码替换,他没有真正的参数属性。
而非内联的函数参数可以自由传递给其他任何函数,他就是一个真正的参数。
内敛的函数只允许传递给另一个内联函数。
-
内联函数所引用的lambda表达式可以return返回。
非内联的函数参数所引用的lambda表达式只能局部返回。
另外,如果在高阶函数lambda表达式中调用了我们的传入的函数参数,此时我们又定义为内联函数,那么就会提示错误
此时需要借助crossinline
关键字
inline fun runRunnable(crossinline block:()->Unit){
Runnable{
block()
}.run()
}
crossinline
关键字的原理是:
内联函数的lambda表达式允许使用return,而高阶函数匿名类中不允许使用return,二者产生冲突。crossinline
关键字保证在内联函数的lambda表达式中一定不会使用return,冲突解决。
高阶函数应用之简化SharedPreference
- kotlin正常使用方法:
val editor=getSharedPreferences("data", Context.MODE_PRIVATE).edit()//获得Shredpreference.Editor对象
editor.putInt("age",18)
editor.putBoolean("sex",true)
editor.apply()
- 使用高阶函数简化:
fun SharedPreferences.opens(block:SharedPreferences.Editor.()->Unit){
//opens方法直接拥有Sharedpreference的上下文,直接通过edit方法获取SharedPreferences.Editor对象
val editor=edit()
editor.block()
editor.apply()
}
定义过高阶函数之后使用:
getSharedPreferences("data", Context.MODE_PRIVATE).opens {
//因为block函数参数前的限定是SharedPreferences.Editor.,因此lambda表达式直接拥有它的上下文环境
putBoolean("sex",true)
putInt("age",18)
}
- 实际上KTX扩展库中已经包含该种用法,直接使用即可
getSharedPreferences("data", Context.MODE_PRIVATE).edit {
putInt("age",18)
putBoolean("sex",false)
}
泛型
泛型类:
class MyClass<T>{
fun myMethod(param:T):T{
return param
}
}
var myClass = MyClass<Int>()
val result = myClass.myMethod(100)
泛型方法:
class MyClass{
fun <T>myMethod(param:T):T{
return param
}
}
var myClass = MyClass()
val result = myClass.myMethod<Int>(100)
由于kotlin优秀的类型推导机制,我们可以省略调用方法时的<Int>
,传参时,自动推导
kotlin允许我们对泛型的类型进行限制,例如可以对myMethod方法的泛型可以设置上界
class MyClass{
fun <T:Number>myMethod(param:T):T{
return param
}
}
若要让泛型的类型不可以为空,只需要将泛型的上界设置为Any