美文网首页
Kotlin之高阶函数

Kotlin之高阶函数

作者: 在海上烧烤 | 来源:发表于2020-03-15 16:08 被阅读0次

与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 表达式中,但退出包含它的函数)称为非局部返回

相关文章

网友评论

      本文标题:Kotlin之高阶函数

      本文链接:https://www.haomeiwen.com/subject/rkfnehtx.html