Kotlin高阶函数入门

Kotlin高阶函数入门

1.1 如何理解Java中的lambda表达式

在讲解Kotlin的高阶函数之前,我们先来了解一下它的前身:Java中的Lambda表达式

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中),使用 Lambda 表达式可以使代码变的更加简洁紧凑。

//Lambda的形式如下:
AInterface a = (param1 , param2) -> {//do some operations}

我们知道对于一个已声明的java对象,我们可以为其赋值;这个“值”,或是基本类型或是一个该对象在堆中的地址。但是上述代码看起来更像是给对象赋了一段“代码块”。那么上述代码块代表的是什么呢?

这就要介绍一下Lambda应用的原理了:

在Java8中不仅推出了Lambda表达式,还拓展了接口的用法,现在接口中可以包括默认方法(该特性的详细信息不做展开),静态方法,和抽象方法。

只有当接口中只包括一个抽象方法时,才能使用Lambda的方式对该接口进行声明。如果我将上述代码写成下面这种形式,你肯定可以很清楚明白代码的含义:

interface AInterface {
    void test (int param1 , int param2);
}
AInterface a = new AInterface {
    @Override
    void test (int prarm1 , int param2){
        //do some operations
    }
}

实际上Lambda表达式就是对上述代码进行了简化,因为该接口中只包括一个抽象方法,所以编译器会将Lambda表达式的内容自动识别成被重写的方法。这同时也说明了为什么在Lambda表达式中想要使用外部的变量必须是final类型的。(同匿名内部类的原理)

到这里可能你对Lambda的基本用法已经了解了,下面让我们进入主题,来研究一下什么是Kotlin的高阶函数。

1.2 通过Java代码理解Kotlin中的高阶函数

在刚开始学习用Kotlin编写Android时,我们可能会在代码中看到下面这个,对于初学者来说很不友好的代码片段:

    fun example(n: Int, m: Int, func: (Int, Int) -> Int) {
        print(func(n, m))
    }

    fun main() {
        example(1, 2) { n, m -> n + m }
    }

不过观察一下就会发现,它要求了一个Lambda表达式作为传入参数。在上述对Lambda表达式的讲解中,我们知道Lambda表达式其实就是一个函数。在Java中也有类似的操作:

package com.suibian.www.java;

public class LambdaDemo {
    public static void main(String[] args) {
        testLambda(() -> {});
    }

    public static void testLambda(Flyable flyable) {
        System.out.println("hello");
    }
}

interface Hello {
    void sayHllo();
}

我们把lambda表达式作为方法的参数进行传递,但是在Java中用一个接口进行接收。而在Kotlin中,我们使用一个更加简洁(不必再声明接口)的函数参数的方式来实现。

因为Kotlin最终还是要编译为Java字节码,所以其实这两种方式得到的最终结果是相同的——实现原理都是使用匿名函数的方式。所以这意味着Kotlin将要产生额外的花销去创建该匿名类实例。

为了解决这个问题,Kotlin提供了内联函数的功能。

1.3 Kotlin中的高阶函数之内联函数

实现内联函数只需要在函数前声明inline关键字即可,

inline val result = test(m , n){n1 , n2 -> n1 + n2}

其原理是,将lambda表达式的操作替换到 test方法 ,中“作为一个正常的语句而执行”,这样就不需要在test中进行传参:

val result = test(m , n)
fun Int test(n1 : Int , n2 : Int){
	return n1 + n2
}

接下来再将 test方法 中的代码全部转移到调用的位置,这样就不需要调用该方法了:

val result = n1 + n2

这样就解决了额外的开销问题,但是inline关键字会将参数中的所有Lambda表达式内联,如果想要使某个函数参数是非内联的,可以在参数前加上noinline关键字。

1.4 内联函数和非内联函数的区别

既然内联函数可以解决资源消耗的问题i,那么为什么还要使用非内联函数?

1.4.1

首先内联函数会在编译的过程中被进行代码替换,可以理解为内联函数并不是一个”真正的“参数。所以在传入了内联函数参数的方法内,该”不是真正的参数“不能被传递给其他方法。

fun difTest(test: () -> Unit) {}

inline fun inlineTest(noinline test: () -> Unit) {
    difTest(test)
}

fun main() {
    inlineTest { print(2) }
}
1.4.2

其次,还是因为内联函数会被代码替换,所以在Lambda表达式的代码块部分可以包括 return 语句。该 return 语句返回的是调用该内联函数的函数,因为代码都被替换到了外层函数中。

此外,无论是内联函数或非内联函数,都可以进行局部返回,相当于 return 了一段代码块:

fun printString(str: String, test: (String) -> Unit) {
    println("printString begin")
    test(str)
    println("printString end")
}

fun main() {
    println("main start")
    val str = ""
    printString(str) { s ->
        println("lambda start")
        if (s.isEmpty()) return@printString
        println(s)
        println("lambda end")
    }
    println("main end")
}
main start
printString begin
lambda start
printString end
main end
1.4.3 crossinline 关键字

来看一段会引发问题的代码:

当外层是内联函数时,我们就可以在传入test的函数参数中加入 return 语句来进行外层代码返回。但如果此时对test函数的调用是在另一个高阶函数内部(非内联函数),则该返回会变成对匿名内部类中的函数进行返回。容易造成语义冲突:

inline fun runRunnable(test: () -> Unit) {
    val Runnable = Runnable {
        test()
    }
    runnnable.run()
}

可以利用crossinline 关键字来解决:

inline fun runRunnable(crossinline test: () -> Unit) {
    val Runnable = Runnable {
        test()
    }
    runnnable.run()
}

该关键字的作用是声明该Lambda表达式一定不会使用 return 语句进行返回。

欢迎各位读者一起讨论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值