与Java不同的是,在Kotlin中函数也是一等公民,高阶函数是将函数用作参数或返回值的函数。
1 、函数类型
在Kotlin中,函数也有自己的类型,可以用于变量、参数还有返回类型
1.1 作为类型
//函数类型为 (Int,Int)->Int
fun add(a:Int,b:Int):Int{
return a+b
}
fun main(){
//定义一个类型为(Int,Int)->Int的变量
var fun1:(Int,Int)->Int
//将add赋值个fun1
fun1=::add
println(fun1(1,1))
}
1.2 作为形参类型
//定义一个函数类型为(Int)->Int类型的函数
fun fun1(a:Int):Int{
return a*a
}
//定义一个形参类型为(Int)->Int的函数
fun fun2(a:(Int)->Int):Int{
var n=1
return a(n)
}
fun main(){
//输出调用
println(fun2(::fun1))
}
1.3 作为返回值类型
//根据不同的类型调用不同的函数
fun calculate(a: Int, b: Int, type: String): () -> Int {
fun add(): Int {
return a + b
}
fun subtract(): Int {
return a - b
}
fun multiply(): Int {
return a * b
}
fun divide(): Int {
return a / b
}
return when (type) {
"subtract" -> ::subtract
"multiply" -> ::multiply
"divide" -> ::divide
else -> ::add
}
}
fun main() {
//接收为一个类型为(Int)->Int类型的函数
var fun1=calculate(1, 2, "multiply")
//再对这个函数进行调用
println(fun1())
}
2 、Lambda表达式
Lambda表达式,也可以称为闭包,其标准写法如下:
(形参)->{
执行语句
}
2.1 Lambda的使用
//普通函数的写法
fun add(a: Int):Int{
return a+a
}
/**
* lambda表达式的写法
* {形参->表达式(方法体)}
*/
//lambda表达式的写法 需指定a的类型
var add = {a:Int-> a+a}
//也可不指定类型 有系统判断
var add1:(Int)->Int={a->a+a}
如果参数只有一个的话 可以省略形参名,形参可以用it代替
var add:(Int)-> Int={it*it}
2.2 Lambda的调用
var add:(Int)-> Int={it*it}
//在 Kotlin 中有一个约定:如果函数的最后一个参数是函数,那么作为相应参数传入的 lambda 表达式可以放在圆括号之外
fun add1(a:Int,b:(Int)->Int){
println(b(a))
}
//如果lambda表达式只有一个参数 那么调用时可以省略括号
fun add2(b:(Int)->Int){
println(b(5))
}
fun main(){
add(2)
add1(1){it+it}
add2{it+it}
}
2.3闭包(Closure)
百度百科
闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
上面是来自百度百科对闭包的解释。
简单来说就是能够读取其他函数内部变量的函数
先来看闭包在kotllin的写法,一般为 函数嵌套函数,这个被嵌套的函数还引用外部的变量,再return一个函数。
fun add():()->Unit{
//外部变量
var a=1
//这是一个闭包函数
var closure=fun(){
println(a++)
}
//返回闭包函数
return closure
}
fun main(){
//调用
var fun1=add()
fun1()
fun1()
fun1()
}
这是输出结果
1
2
3
闭包的作用
1、保护函数内的变量安全
2、在内存中维持一个变量
2.4 内联函数
虽说使用Lambda会使代码更加的简洁,但它也不是没有缺点的,当我们调用Lambda函数时,他都会被编译成一个匿名对象,这是空间开销,对他的内存调用和虚拟分配则有了时间开销,所以Kotlin引进了内联(inline),来避免这些开销。
我们先来看一下代码
//定义一个Lambda函数
fun add(a:Int,b:Int):()->Unit{
return{
println(a+b)
}
}
fun main(){
println("lambda调用前")
val a= add(1,1)
a()
println("lambda调用后")
}
输出结果如下:
lambda调用前
2
lambda调用后
我们再来看一下经过Kotlin ByteCode 反编译成的Java代码
public final class Demo1Kt {
public static final void main() {
String var0 = "lambda调用前";
boolean var1 = false;
System.out.println(var0);
Function0 a = add(1, 1);
a.invoke();
String var4 = "lambda调用后";
boolean var2 = false;
System.out.println(var4);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
@NotNull
public static final Function0 add(final int a, final int b) {
return (Function0)(new Function0() {
// $FF: synthetic method
// $FF: bridge method
public Object invoke() {
this.invoke();
return Unit.INSTANCE;
}
public final void invoke() {
int var1 = a + b;
boolean var2 = false;
System.out.println(var1);
}
});
}
}
如上面代码所示:Lambda函数被编译成一个匿名函数,但是如果使用inline的话
//定义一个Lambda函数
fun add(a:Int,b:Int):()->Unit{
return{
println(a+b)
}
}
代码编译成Java代码如下:
public final class Demo1Kt {
public static final void main() {
String var0 = "lambda调用前";
boolean var1 = false;
System.out.println(var0);
byte a$iv = 1;
int b$iv = 1;
int $i$f$add = false;
Function0 a = (Function0)(new Function0(a$iv, b$iv) {
// $FF: synthetic field
final int $a;
// $FF: synthetic field
final int $b;
// $FF: synthetic method
// $FF: bridge method
public Object invoke() {
this.invoke();
return Unit.INSTANCE;
}
public final void invoke() {
int var1 = this.$a + this.$b;
boolean var2 = false;
System.out.println(var1);
}
public {
this.$a = var1;
this.$b = var2;
}
});
a.invoke();
String var6 = "lambda调用后";
boolean var7 = false;
System.out.println(var6);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
@NotNull
public static final Function0 add(int a, int b) {
int $i$f$add = 0;
return (Function0)(new Function0(a, b) {
// $FF: synthetic field
final int $a;
// $FF: synthetic field
final int $b;
// $FF: synthetic method
// $FF: bridge method
public Object invoke() {
this.invoke();
return Unit.INSTANCE;
}
public final void invoke() {
int var1 = this.$a + this.$b;
boolean var2 = false;
System.out.println(var1);
}
public {
this.$a = var1;
this.$b = var2;
}
});
}
}
引用一下:白话kotlin:内联函数助你提升运行效率解释,感谢
在kotlin中,函数就是对象,当你调用某个函数的时候,就会创建相关的对象。
你看,这就是空间上的开销!
当你去调用某个函数的时候,虚拟机会去找到你调用函数的位置。然后执行函数,然后再回到你调用的初始位置。
你看,这就是时间上的开销!
ok,现在,我直接把调用的函数里面的代码放到我调用的地方,省去寻找调用函数的位置的时间。
禁用内联(noinline)
使用inline修饰函数后,所有传入的lambda表达式或者函数都会被内联化,如果想要某个函数不要被内联化,则需要使用noline来修饰。
代码如下所示:
inline fun test(a:()->Unit,noinline b:()->Unit){}
非局部返回
当Lambda函数想要通过return进行退出时,需要使用return@xx标签,如果使用return直接返回则会报错,代码如下所示:
fun test(say: () -> Unit) {
println("哈哈")
say()
}
fun test2() {
test {
println("呵呵")
return@test
}
println("嘿嘿")
}
fun main() {
test2()
}
输出结果如下:
哈哈
呵呵
嘿嘿
但是如果使用inline修饰的话,使用return返回是被允许的,代码如下所示:
inline fun test(say: () -> Unit) {
println("哈哈")
say()
}
fun test2() {
test {
println("呵呵")
return
}
println("嘿嘿")
}
这种返回(位于 lambda 表达式中,但退出包含它的函数)称为非局部返回











网友评论