新手上路,Kotlin学习笔记(七)---Lambda作为形参和返回值的使用

        入行没几年的小码农,近期学习Kotlin,做一份笔记记录,此文依据《Kotlin实战》这本书的流程记录,部分示例内容均摘自《Kotlin实战》,记下自己的理解,本篇记录在Kotlin中使用Lambda作为形参和返回值的时候,该如何使用。



        Kotlin学习笔记系列

        新手上路,Kotlin学习笔记(一)-- Kotlin入门介绍

        新手上路,Kotlin学习笔记(二)---方法(函数)部分

        新手上路,Kotlin学习笔记(三)---类、对象、接口

        新手上路,Kotlin学习笔记(四)---Lambda表达式在Kotlin中的使用

        新手上路,Kotlin学习笔记(五)---Kotlin中的类型系统

        新手上路,Kotlin学习笔记(六)---运算符重载和其它约定

        新手上路,Kotlin学习笔记(七)---Lambda作为形参和返回值的使用

        新手上路,Kotlin学习笔记(八)---泛型的使用

        新手上路,Kotlin学习笔记(九)---注解和反射


一、高阶函数(方法)

        什么是高阶函数呢?按照定义来说,高阶函数就是将另一个函数作为返回值或者参数的函数。

所以,我们在前面看到的,以Lambda作为参数的函数,就可以认为是高阶函数。

        我们知道,返回值或者参数,都是由类型的,那么接下来我们来看一个函数(方法)是如何声明的它的类型的。

    fun testLambda() {
        val sum = { x: Int, y: Int -> x + y }
        val sum2 : (x: Int , y : Int) -> Int = {x , y -> x + y} //sum 和 sum2是相同的,sum隐藏了变量的类型
        
        val prt = { print("abc")}
        val prt2 : () -> Unit = { print("abc")} //prt 和 prt2是相同的,这个是入参和返回值都没有的场景
    }

可以看到,函数式的声明是由参数和返回值组成的,参数在括号中,返回值在->右侧。没有的时候示例如prt2那样。同样的,当后面有明确的lambda表达式,编译器会自动帮我们智能识别类型,可以省略掉。让我们看一下之前在源码中见到的以函数为参数的方法。

/**
 * Performs the given [action] on each element.
 */
@kotlin.internal.HidesMembers
public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
    for (element in this) action(element)
}
        上面的这个源码就是我们熟悉的foreach方法,当我们对集合调用foreach方法的时候,会传入一个函数式的参数,参数类型很简单,就是 (T) -> Unit,一个将集合内单个元素作为入参,不需要返回值的实现即可,刚好完成了我们的遍历集合,执行某个流程的操作,非常简单实用。


        接下来我们继续看,当一个函数的返回值是另一个函数的时候,又该怎么办呢?

    fun getShowInfo(person: Person): (person: Person) -> Unit {
        return when (person) {
            is Student -> { p -> print("name = ${p.name} , + age = + ${p.age}") }
            is Teacher -> { p -> print("name = ${p.name}") }
            else -> {p -> print("other")}
        }
    }

        上面这个示例,就是一个返回值为一个函数的场景,我们的返回值是(person : Person) -> Unit类型,然后对应实现中,当对象实际是教师或者学生的时候,会返回不同的实现方式,这个方法刚好可以用于我们上个例子中的foreach的入参,此时就做出了一个根据实际类型,将不同的实现传给foreach的功能。

        将参数和返回值转换为一个方法,用Lambda表达式非常的实用和方便,可以让我们的代码看起来更加的简洁,那么在效率上如何呢?接下来我们来看如何消除Lambda在运行时的开销。


二、内联函数

        当我们使用Lambda的时候,实际上编译器会以java匿名内部类的形式去处理,那么频繁的时候Lambda,不停地创建类,一定会让我们的程序效率降低,这样是得不偿失的,那么,我们如何避免这样的事情发生呢?

        答案就是使用内联函数!

        内联函数为什么能解决上述的问题呢?什么是内联函数呢?

        内联函数的作用就是,将参数或者返回值中的函数,直接以代码块的形式放置在代码中使用,而不再像Java匿名内部类那样工作,这样就会提升很大的性能。而声明内联函数的方式也很简单,就像第二个例子中的源码那样,在声明的时候,加上inline关键字即可。

        当然,内联函数也是有限制的,当我们需要将这个函数式的参数保存下来的时候,就必须依赖于一个对象,所以只能使用匿名内部类的形式,如果将内联函数的Lambda作为参数保存下来,编译器将会报错!

        那么就出现了一个疑问,如果我们有多个函数式的参数在使用,但是只有一个需要保存,那么就不能使用内联函数了吗?

        答案当然是可以使用了,只需要将那个保存的函数式参数声明为noinline即可。

    inline fun testLambda2(action : () -> Unit , noinline action2 : (person : Person) -> Unit)
    {
        val act2 = action2
        //val act = action  //因为action是内联的,此处编译器将报错
    }


三、高阶函数中的控制流

        当我们使用高阶函数后,Lambda表达式中的return是怎么工作的呢?是终结Lambda里面的逻辑还是停止掉整个方法呢?

        当发现我们既有需要停掉当前Lambda流程,又有可能需要停掉整个方法的需求时,单纯的一个return并不能完成我们的需求, 此时Kotlin为我们提供了两种return的方式,完成我们的两个需求。在Lambda中直接使用return的时候,是会终止这个Lambda所在方法的流程,当我们需要单独停止这个Lambda的时候,需要使用标签返回,格式是

        return + @ + lambda表达式的名称(默认是方法名)

    fun lambdaReturn(people: ArrayList<Person>) {
        people.forEach()
        label@{
            if(it.age < 18)
            {
                //return@forEach  如果没有显式声明lambda的名称,使用默认的方法名即可
                return@label
            }
            if(TextUtils.equals(it.name,"Bob"))
            {
                return
            }
        }
    }
        tips:当我们使用匿名内部类而不是Lambda实现的时候,return就和我们通常Java中看到的一样,直接返回这个匿名内部类对应的方法,而不是外部的方法。


        好了,今天的内容到此为止,下一章将学习Kotlin中泛型如何使用!


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值