Kotlin高阶函数中的控制流
当你开始使用lambda去替换像循环这样的命令式代码结构时,很快便会遇到return表达式的问题。把一个return语句放在循环的中间是很简单的事情。
1.lambda中的返回语句:从一个封闭的函数返回
来比较两种不同的遍历集合的方法,在下面代码清单中,很明显如果一个人的名字是Alice,就该从函数lookForAlice返回。
data class Person(val name: String, val age: Int)
val paple = listOf(Person("Alice", 29), Person("Bob", 31))
fun lookForAlice(people:List<Person>){
for (person in people){
if (person.name=="Alice"){
LogS("Found!")
return
}
}
}
lookForAlice(paple)//Found!
使用forEach迭代重写这段代码安全吗?return余军还是一样的表现吗?
是的,正如下面所展示的,使用forEach是安全的。
fun lookForAlice(people:List<Person>){
people.forEach {
if (it.name=="Alice"){
LogS("Found!")
return
}
}
}
如果你在lambda中使用return关键字,它会从调用lambda的函数中返回,并不只是用lambda中返回。这样的return语句叫做非局部返回,因为它从一个比包含return的代码块更大的代码块中返回。
为了理解这条规则背后的逻辑,想想Java函数中在for循环或者synchronized代码块中使用return关键字。显然会从函数中返回,而不是从循环或者代码块中返回。当使用lambda作为参数的函数的时候Kotlin保留了相同的行为。
2.从lambda返回:使用标签返回
也可以在lambda表达式中使用局部返回。lambda中的局部返回跟for循环中的break表达式相似。它会终止lambda的执行,并接着从调用lambda的代码处执行。要区分局部返回和非局部返回,要用到标签。想从一个lambda表达式中返回你可以标记它,然后return关键字后面引用这个标签。
fun lookForAlice(people:List<Person>){
people.forEach lable@{
if (it.name=="Alice"){
return@lable
}
}
}
要标记一个lambda表达式,在lambda的花括号之前放一个标签名,接着放一个@符号。要从一个lambda返回,在return关键子够放一个@符号,接着放标签名。
另外一种选择是,使用lambda作为参数的函数的函数名可以作为标签。
fun lookForAlice(people:List<Person>){
people.forEach{
if (it.name=="Alice"){
return@forEach
}
}
}
如果你显示的指定了lambda表达式的标签,再使用函数名作为标签没有任何效果。一个lambda表达式标签数量不能多于一个。
3.匿名函数:默认使用局部返回
匿名函数是一种不同的用于编写传递给函数的代码块的方式。
fun lookForAlice(people: List<Person>) {
people.forEach (
fun(person) {
if (person.name == "Alice") {
return
}
}
)
}
在filter中使用匿名函数
people.filter(fun (person):Boolean{
return person.age<30
})
匿名函数和普通函数有相同的指定返回值类型的规则。代码块体匿名函数需要显示的指定返回类型,如果使用表达式函数体,就可以省略返回类型。people.filter(fun (person)=person.age<30)
在匿名函数中,不带标签的return表达式会从匿名函数中返回,而不是从包含匿名函数的函数返回。这条规则很简单:return从最近的使用fun关键字,所以lambda中的return从最外层的函数返回。匿名函数使用了fun,因此,在前一个例子中匿名函数是最近的符合规则的函数。索引,return表达式从匿名函数返回,而不是从最外层的函数返回。