Kotlin进阶知识(八)——高阶函数中的控制流

888 阅读3分钟

一、lambda中的返回语句:从一个封闭的函数返回

  • 在一个普通循环中使用return
fun lookForAlice(people: List<PersonNew>) {
    for(person in people) {
        if(person.name == "Alice") {
            println("Found")
            return
        }
    }
    // 如果“people”中没有Alice,这一行就会被打印出来
    println("Alice is not found")
}

fun lookForAliceTest() {
    lookForAlice(people)
}

// 输出结果
Found!
  • 在传递给forEachlambda中使用return
fun lookForAliceByForEach(people: List<PersonNew>){
    people.forEach {
        if(it.name == "Alice") {
            println("Found!")
            return
        }
    }
    println("Alice is not found")
}

fun lookForAliceByForEachTest() {
    lookForAliceByForEach(people)
}

非局部返回:在lambda中使用return关键字,它会从调用lambda的函数中返回,并不只是从lambda中返回

注意非局部返回,是从一个比包含return的代码块更大的代码块返回了。

注意:只有在以lambda作为参数的函数是内联函数的时候才能从更外层的函数返回。

在一个非内联函数lambda使用return表达式不允许的。一个非内联函数可以把传给它的lambda保存在变量中,以便在函数返回以后可以继续使用,这个时候lambda想要去影响函数的返回已经太晚了。

二、从lambda返回:使用标签返回

可以在lambda表达式中使用局部返回。lambda中的局部返回for循环中的break表达式相似。它会终止lambda的执行,并接着从调用lambda的代码处执行。

要区分局部返回和非局部返回,要用到标签。想从一个lambda表达式处返回你可以标记它,然后在return关键字后面引用这个标签。

  • 用一个标签实现局部返回

fun lookForAliceByLabel(people: List<PersonNew>){
    people.forEach label@ {
        // return@label引用了
        if(it.name == "Alice") return@label
    }
    // 这一行总是会被打印出来
    println("Alice might be somewhere")
}

fun lookForAliceByLabelTest() {
    lookForAliceByLabel(people)
}

// 输出结果
Alice might be somewhere
  • 标记一个lambda表达式,在lambda花括号之前放一个标记名(可以是任何标识符),接着放要给**@字符**。

  • 要从一个**lambda返回**,在**return关键字后放一个@符号**,接着放标签名,如图1:

图1:用“@”符号标记一个标签从一个lambda返回

  • 用函数名作为return标签
fun lookForAliceByLabel1(people: List<PersonNew>) {
    people.forEach {
        // return@forEach 从lambda表达式返回
        if(it.name == "Alice") return@forEach
    }
    println("Alice might be somewhere")
}

显式地指定了**lambda表达式的标签**,再使用函数名作为标签没有任何效果。一个lambda表达式的标签数量不能多于一个

  • 带标签的“this”表达式
fun lambdaTest() {
    // 这个lambda的隐式接受者可以通过this@sb访问
    println(StringBuilder().apply sb@ {
        // “this” 指向作用域内最近的隐式接受者
        listOf(1, 2, 3).apply {
            // 所有隐式接受者都可以被访问,外层的接受者通过显式的标签访问
            this@sb.append(this.toString())
        }
    })
}

同样的规则也适用于**this表达式的标签**,带接受者的lambda——包含一个隐式上下文对象的lambda可以通过一个this引用去访问。

和return表达式中使用标签一样,可以显示地指定lambda表达式的标签,也可以使用函数名作为标签。

三、匿名函数:默认使用局部返回

匿名返回是一种不同的用于编写传递给函数的代码块的方式。

  • 在匿名函数汇总使用return
fun lookForAliceByAnonymousFunction(people: List<PersonNew>) {
    people.forEach(fun (person) {
        // 使用匿名函数取代lambda表达式
        if(person.name == "Alice") return

        // “return”指向最近的函数:一个匿名函数
        println("${person.name} is not Alice")
    })
}

fun lookForAliceByAnonymousFunctionTest() {
    lookForAliceByAnonymousFunction(people)
}

//  输出结果
Bob is not Alice

匿名函数看起来跟普通函数相似,除了它的名字和参数类型被省略了外。

匿名函数中,不带标签return表达式会从匿名函数返回,而不是从包含匿名函数的函数返回

这条规则很简单:

  • return最近使用fun关键字声明函数返回。
  • lambda表达式没有使用**fun关键字**,所以lambda中的return从最外层的函数返回