高阶函数是现代编程语言中一个重要的概念。它们不仅在函数式编程中扮演着关键角色,也被广泛应用于其他编程范式。高阶函数的核心思想是将函数作为参数传递给其他函数或从函数中返回另一个函数。通过这种方式,高阶函数使得代码更加模块化,可复用性更强,并且可以极大地提升代码的可读性和简洁性。本文将介绍高阶函数的基础概念、优点以及如何在不同场景下使用它们。无论你是一名有经验的程序员还是初学者,都能从本文中获得有益的知识,进一步提高自己的编程技能。
Kotlin-lamda与高阶函数
Kotlin函数先有输入后有输出
//java代码先有输出再有输入
public Object getStudent(String name) {
...
}
//kotlin代码现有输入再有输出
public fun getStudent(name:String):Any {
...
}
Kotlin函数声明
声明一个无返回的kotlin函数
//声明一个函数,()->Unit相当于java中的void声明
//类似c++中的头文件的感受,需要对应的实现
var method : ()->Unit
var method2 : (Int,Int)->Unit
var method3 : (String,Double) -> Any
对应的java实现
public void method() {
...
}
public void method2(int a,int b){
...
}
public Object method3(String a,double b) {
...
}
更进一步的声明
直接实现,类型kotlin可以根据类型推导确定
//匿名函数
var method01 = { println("我是method01函数") }
//等价于
var method01 : ()->Unit = { println("我是method01函数") }
对应的java实现
public void method01() {
System.out.println("我是method01函数")
}
有返回值的方法声明
var method02 = { "I'm method02"}
equels
public String method02(){
return "I'm method01";
}
字符串模板
var method03 = {str:String -> println("this is $str")
equels
public void method03(String str) {
System.out.println("this is " + str);
}
另外一种声明
var method04 = {num1:Int,num2:Int - >num1.toString() + num2.toString()}
equels
public String method4(int num1,int num2){
retrun String.valueof(num1) + String.valueof(num2);
}
先声明后实现
var method06 :(Int) -> String
method06 = fun(value /*:Int*/)/*String*/ = value.toString
也可以声明时实现
var method7:(Int)->String = { a /*:String*/ -> "$a"}
//equels
//"it" is automatic generation of system
var method7:(Int)->String = {/*it ->*/ "$a"}
如果有参数没有使用
_拒收
var method08 :(Int,Int)->Unit = { _,b->
print("the second para is $b")
}
java三元运算符kolin替代
var method09 = {sex:Char ->if(sex == 'male') print("it's male") else print("it's female")
复写
var method10 = {number:Int -> println("this is $number")}
//复写不能改变其类型
method10 = {println("is it $it?")}
Kotlin中的拓展函数
给String添加拓展函数,这也是Compose的实现方式,关于这点可参考为 Kotlin 的函数添加作用域限制(以 Compose 为例)
var method11: String.() -> Unit = {
//this指的是String对象本身
println(this)
}
如果给Any类型添加拓展函数,可以给作为默认的函数拓展,拓展函数可以传入参数,可以使用invoke调用,第一个参数为自身
Lamda表达式和普通函数的区别
lamda表达式最后一行为返回值
例如
var methodK02/*: (Int) -> Int*/ = { a: Int -> a + 1 }
函数体没有return无法进行类型推导
fun k02(a: Int)/*:Unit*/ {
val b = a + 1
b
}
fun k03(a: Int):Int {
val b = a + 1
return b
}
高阶函数
函数 -> (para->Unit),即函数中的函数
举例 run函数
//返回类型为Unit
var method12 = kotlin.run { println("this is a code") }
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
run函数是一个内联函数
在 Kotlin 中,inline
是一个关键字,用于声明内联函数。使用 inline
修饰符可以通过将函数的代码插入调用处来减少函数调用的开销。这样可以提高程序的执行效率,并避免过多的函数调用带来的性能损失。
常见的内联函数包括高阶函数和 Lambda 表达式,它们在编译时会被转换为匿名类或对象,这些类或对象的实例在运行时创建。当使用这些函数时,编译器会创建一个匿名类或对象的实例来代表这些函数,并在运行时进行调用。但是,使用 inline
关键字修饰的函数将会在编译时被直接复制到调用处,而不是生成匿名类或对象。这样可以避免对象分配和函数调用的开销,从而提高程序的执行效率,并减少内存的使用。因此,内联函数可以消除这些额外的对象分配和函数调用的开销。
需要注意的是,虽然内联函数能够消除对象分配和函数调用的开销,但在某些情况下也可能会带来一些副作用。例如,在使用大量的内联函数时,可能会使编译后的代码变得更大,从而增加应用程序的安装包大小。
//类型为String -> Unit
fun k01() = { n1: String ->
println(n1)
}
fun main(args: Array<String>) {
println("Hello World!")
// Try adding program arguments via Run/Debug configuration.
// Learn more about running applications: https://www.jetbrains.com/help/idea/running-applications.html.
println("Program arguments: ${args.joinToString()}")
val test = StringExtra()
test.method12
//简易调用
test.k01()("123")
}
说到简易调用,可以额外举一个例子
var method21: Double.(Double, Double) -> Unit = { a, b -> println("the result is ${this + a + b}") }
println(method21.invoke(55.5, 6.6, 777.7))
55.5.method21(6.0, 8.8)
关于Kolin中Float和Double的类型推导需要注意,Kotlin中小数默认均为Double,只有加f才能声明为Float类型,不过默认使用 Double 能省很多麻烦。除非特殊需要,尽量不要指定为 Float。
val a/*:Double*/ = 6.0
val b/*:Float*/ = 6.0f
fun aa(){}和fun aa2() ={}的区别
第一个为一个函数
第二个接受一个匿名函数,
共同点,执行现象是一样的。
fun aa(){}
fun aa2()/*: () -> Int*/ = { 1 }
var aa3 = aa2()
var aa4 = ::aa