小狮子的Kotlin学习之路(十六)

函数类型与Lambda表达式

在Kotlin高阶函数中,涉及到了两个比较重要的概念,那就是Kotlin的函数类型Lambda表达式

其实,Lambda表达式是函数类型的一个实例。

怎么说呢,Kotlin中把类似的声明(T) -> R称作函数类型。具体点的比如(Int) -> String,就是说这里的参数是一个函数类型,函数的入参为Int类型,返回值是String类型。

另外,如果函数类型有多个入参,比如(A, B) -> C。

如果没有参数的函数类型,则表示为() -> R。

需要注意的是,函数类型中,如果返回值为Unit的,不能和函数一样省略掉。

    fun func(a: Int, p: (p: String) -> Unit) {

    }

这样是OK的,但是如果Unit不写,编译器就会提示缺少类型声明。

另外中函数类型的表示方法为A.(B) -> C,表示A调用的是一个(B)-> C的返回值,这种情况下,很多时候用于C是一个函数类型。也称作“带有接收者的函数字面值”,听起来很玄乎的,其实和扩展函数类似。

val sum: Int.(Int) -> Int = { other -> plus(other) }

是不是看不太懂,用扩展函数重写一下

    fun Int.p(p: Int) {
        plus(p)
    }

或者写成这样

val sum = fun Int.(other: Int): Int = this + other

再来重新一下第一个

     val sum: Int.(Int) -> Int = {
         other -> this.plus(other)
     }

this就代表的是Int,最终就是A类型调用它的扩展函数。看一个比较高大上的

@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

被称之为Kotlin语法糖之一的let函数。关于Kotlin语法糖,我们下一篇单独来说,看完之后,你就会对Kotlin深爱。

另外有一个比较特殊的函数类型,使用suspend来修饰的,称为挂起函数。挂起函数用在协程当中,协程是Kotlin异步的最佳实践。我们后面会抽专门的篇幅来介绍协程。

关于函数类型,还有几个点:

  1. 如需将函数类型指定为可空,请使用圆括号:((Int, Int) -> Int)?
  2. 函数类型可以使用圆括号进行接合:(Int) -> ((Int) -> Unit)
  3. 箭头表示法是右结合的,(Int) -> (Int) -> Unit 与前述示例等价,但不等于 ((Int) -> (Int)) -> Unit
  4. 可以通过使用类型别名给函数类型起一个别称。
typealias ClickHandler = (Button, ClickEvent) -> Unit

有别称的函数类型,后续调用可以直接使用别称。

关于函数类型说完了,总结一下,如果一个函数需要传入函数类型,有哪些办法可以得到函数类型的实例传入呢?

  1. Lambda表达式和匿名函数。
  2. 使用已有声明的可调用引用。
  • 顶层、局部、成员、扩展函数:::isOdd、 String::toInt
  • 顶层、成员、扩展属性:List<Int>::size
  • 构造函数:::Regex
  1. 使用实现函数类型接口的自定义类的实例,如
class IntTransformer: (Int) -> Int {
    override operator fun invoke(x: Int): Int = TODO()
}

val intFunction: (Int) -> Int = IntTransformer()

需要注意的是,在获取函数类型实例时,如果有足够的信息使得编译器能够推断出类型的话,其类型可以省略。

函数类型的值可以通过其 invoke(……) 操作符调用:f.invoke(x) 或者直接 f(x)

上面已经提及好几次Lambda表达式了,接下来,就看看Lambda表达式是如何定义的。

先引用官方原话:lambda 表达式与匿名函数是“函数字面值”,即未声明的函数, 但立即做为表达式传递。

然后具体举例说明。

说是“函数字面值”,也就是说Lambda表达式就相当于一个变量,用没有名字的函数表示了,相当于

{a, b -> a + b}

简单来说,就一段代码块。

对于匿名函数来说,就比lambda表达式多了一个fun关键字。

            val sum: (Int, Int) -> Int = {a, b ->
                a + b
            }
            val sum2 = fun (a: Int, b: Int): Int = a + b

另外,当lambda表达式的参数唯一时,可以省略圆括弧。

如可以使用的标准库函数

run {print("Hello")}

另外,lambda表达式参数唯一时,如果编译器自己可以识别出签名,也可以不用声明唯一的参数并忽略 ->。 该参数会隐式声明为 it

            val sum: (Int) -> Int = {
                1
            }

如果使用编译器,则可以看到编译器对应的提示

Lambda 表达式或者匿名函数(以及局部函数和对象表达式) 可以访问其闭包 ,即在外部作用域中声明的变量。

var sum = 0
ints.filter { it > 0 }.forEach {
    sum += it
}
print(sum)

是不是觉得Kotlin的魅力越来越大啦

关于函数类型与Lambda表达式,就说到这儿,下一篇,学习Kotlin的语法糖,让你彻底爱上它。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值