lambda

lambda表达式


根据图片可以比较清晰的了解lambda的语法,他通过->将参数和函数体分开,参数不用带小括号,整体都用一个中括号括起来,箭头右边写函数体,函数体可以写多行,最后一行做为表达式的值传递回去。
为了让你加深理解。

  • 实例1:
    1
    2
    3
    4
    fun main(args: Array<String>) {
    //{x:Int,y:Int -> x+y}本身就是一个函数
    println({x:Int,y:Int -> x+y}(1,2))
    }
    以上内容,肯定了lambda函数表达式,他本身就是一个函数,可以直接使用。像一个正常函数一样。
    但是这样子意义不大,就像是刻意为之
  • 实例2:
    1
    2
    3
    4
    fun main(args: Array<String>) {
    var sum = {x:Int,y:Int -> x+y}
    println(sum(1,3))
    }
    kotlin作为函数式编程语言。他可以直接把函数传递给一个变量存储。而且该变量可以被其他类,函数所接受。这也是kotlin的一大特点之一。这个被保存到函数同样可以像正常函数一样使用。

注意点

通常使用lambda函数有一些特点
0. 如果传递进去的参数类型是可知的,可以直接省略掉该参数的类型声明,而直接交给编译器,自动推导

  1. 如果只有一个参数,那么这个参数可以直接省略,用it来代替
  2. 如果函数被作为一个方法的参数,而且是最后一个参数,那么这个函数可以被写在小括号后面,单独列出来。
  3. 如果一个类或者一个方法接受一个函数作为变量,而且只接受一个变量,那么可以直接省略小括号,直接放lambda函数上去(当然,不用lambda也行,用其他的函数体)

在作用域中访问变量

在kotlin中,他的lambda函数不仅可以获取到局部变量,还可以修改这个局部变量。我们称这些变量被lambda捕捉

1
2
3
4
5
6
7
8
9
10
11
12
fun main(args: Array<String>) {
val num0 = "测试获取val数据"
var num1 = 1
var add = {
println(num0)
num1++
}
repeat(4) {
println(num1)
add()
}
}

展现结果为:

实现细节:

  1. 首先,在java中,他只允许你捕捉final变量,也就是kotlin中的val变量,他们不可被修改,只能读取。
  2. 当你捕捉val变量时,系统会将这个值和lambda代码一起存储。
  3. 当你要捕获var变量,并进行更改时,他会采用两种方法之一,要么声明一个单元素的数组,其中存放可变值。或者创建一个包装类的实例,把这个var变量作为该实例的一个属性,而且可以修改。

    在上述图片中,第一段代码就是对第二段代码的解释。
    当kotlin中捕获一个var变量时,他会创建一个val的包装类,然后存储该包装类的实例,而这个包装类因为时val的,所以很好获取。

注意事项

如果 lambda 被用作事件处理器或者用在其他异步执行的情况,对局部变量的修改只会在 lambda 执行的时候发生。
意思是说,如果你这个lambda函数作为异步处理的,所以你修改的这个局部变量最好能够通过其他类,或者全局保存。不然他将无法修改成功。

成员引用

他的使用场景是,当你需要把一个已经定义好的函数抠下来,作为一个参数传递给其他需要使用这个函数参数的地方。

这种表达式称为成员引用,用双冒号把你要引用的成员(一个属性或者一个方法)和类名称隔开。

如果是顶层函数,可以直接用::方法名获取

构造方法
可以用构造方法引用 存储或者延期执行创建类实例的动作 构造方法引用的形式,是在双冒号后指定类名称:
不难理解,所以直接跳过

详解成员引用

成员引用和调用该函数的 lambda 具有一样的类型,所以可以互换使用:这句话请深刻记忆,他很重要。

顶层函数

直接从顶层方法中获取的方法,他不需要再接受额外的参数,可以直接通过run方法去执行他的函数体,如下:

1
2
3
4
5
6
fun salute() = println("Salute !")
···
fun main(){
salute()
run(::salute)
}

这两个语句的执行结果一致,都会输出Salute !
如果这个函数有参数的话,而run方法默认是不带参数的。
源码如下:

1
2
3
4
5
6
7
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}

因此可以通过其他方法,比如用一个参数去接受函数体,然后像正常函数一样执行。

非顶层函数

如果是某个类中的函数,那么他会有一个限制。
那就是如果你要使用这个函数的话,他一定需要传入一个他该类的实例进去,不然就会语法报错。
例如:

1
2
3
4
5
6
7
8
9

class Car {
fun getSex() = println("不男不女")
}

fun main() {
var getSex = Car::getSex
getSex(Car())
}

他要求必须传入一个实例进去。
但是在kotlin1.1后版本更新,允许进行绑定引用
也就是直接引用来自实例的方法。
如下:

1
2
3
4
5
6
7
8
9
10
11
import kotlin.reflect.KFunction1

class Car {
fun getSex() = println("不男不女")
}

fun main() {
var car = Car()
var getSex = car::getSex
getSex()
}

以上