你可能会好奇,对Kotlin中的一些用法感到困惑,比如:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
btn_login.setOnClickListener({
val phone = et_phone.text.toString().trim()
val password = et_password.text.toString().trim()
if (TextUtils.isEmpty(phone)) {
ToastUtils.showShortToast(this, "手机号不能为空")
return@setOnClickListener
}
if (!ValidateUtils.isMobile(phone)) {
ToastUtils.showShortToast(this, "请输入正确手机号")
return@setOnClickListener
}
if (TextUtils.isEmpty(password)) {
ToastUtils.showShortToast(this, "密码不能为空")
return@setOnClickListener
}
if (password.length < 6 || password.length > 16) {
ToastUtils.showShortToast(this, "请输入6-16位密码")
return@setOnClickListener
}
getCampus(phone, password)
})
btn_forget_password.setOnClickListener({
val phone = et_phone.text.toString().trim()
startActivity(
Intent(this@LoginActivity, ForgetPasswordActivity::class.java).putExtra("phone",
phone))
})
上面是我从登录功能中截取的一段代码,我们来着重看下面这句:
if (password.length < 6 || password.length > 16) {
ToastUtils.showShortToast(this, "请输入6-16位密码")
return@setOnClickListener
}
这个@setOnClickListener是个什么鬼呢?
其实我们被编译器的颜色误导了,这个@标记的是return,也就是 return@
Kotlin的返回与跳转
Kotlin 中存在 3 种跳出程序流程的表达式:
return. 默认行为是, 从最内层的函数或 匿名函数 中返回.
break. 结束最内层的循环.
continue. 在最内层的循环中, 跳转到下一次循环.
所有这些表达式都可以用作更大的表达式的一部分:
val s = person.name ?: return
这些表达式的类型都是 Nothing 类型.
Break 和 Continue 的位置标签
Kotlin 中的任何表达式都可以用 label 标签来标记. 标签由标识符后面加一个 @ 符号构成, 比如: abc@, fooBar@ 都是合法的标签(参见 语法). 要给一个表达式标记标签, 我们只需要将标签放在它之前:
loop@ for (i in 1..100) {
// ...
}
然后, 我们就可以使用标签来限定 break 或 continue 的跳转对象:
loop@ for (i in 1..100) {
for (j in 1..100) {
if (...) break@loop
}
}
通过标签限定后, break 语句, 将会跳转到这个标签标记的循环语句之后. continue 语句则会跳转到循环语句的下一次循环.
使用标签控制 return 的目标
在 Kotlin 中, 通过使用字面值函数(function literal), 局部函数(local function), 以及对象表达式(object expression), 允许实现函数的嵌套. 通过标签限定的 return 语句, 可以从一个外层函数中返回. 最重要的使用场景是从 Lambda 表达式中返回. 回忆一下我们曾经写过以下代码:
//sampleStart
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return // 非局部的返回(non-local return), 直接返回到 foo() 函数的调用者
print(it)
}
println("this point is unreachable")
}
//sampleEnd
fun main(args: Array) {
foo()
}
这里的 return 会从最内层的函数中返回, 也就是, 从 foo 函数返回. (注意, 这种非局部的返回(non-local return), 仅对传递给 内联函数(inline function) 的 Lambda 表达式有效.) 如果需要从 Lambda 表达式返回, 我们必须对它标记一个标签, 然后使用这个标签来指明 return 的目标:
//sampleStart
fun foo() {
listOf(1, 2, 3, 4, 5).forEach lit@{
if (it == 3) return@lit // 局部的返回(local return), 返回到 Lambda 表达式的调用者, 也就是, 返回到 forEach 循环
print(it)
}
print(" done with explicit label")
}
//sampleEnd
fun main(args: Array) {
foo()
}
这样, return 语句就只从 Lambda 表达式中返回. 通常, 使用隐含标签会更方便一些, 隐含标签的名称与 Lambda 表达式被传递去的函数名称相同.
//sampleStart
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@forEach // 局部的返回(local return), 返回到 Lambda 表达式的调用者, 也就是, 返回到 forEach 循环
print(it)
}
print(" done with implicit label")
}
//sampleEnd
fun main(args: Array) {
foo()
}
或者, 我们也可以使用 匿名函数 来替代 Lambda 表达式. 匿名函数内的 return 语句会从匿名函数内返回.
//sampleStart
fun foo() {
listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
if (value == 3) return // 局部的返回(local return), 返回到匿名函数的调用者, 也就是, 返回到 forEach 循环
print(value)
})
print(" done with anonymous function")
}
//sampleEnd
fun main(args: Array) {
foo()
}
注意, 上面三个例子中局部返回的使用, 都与通常的循环中的 continue 关键字的使用很类似. 不存在与 break 直接等价的语法, 但可以模拟出来, 方法是增加一个嵌套的 Lambda 表达式, 然后在它内部使用非局部的返回:
//sampleStart
fun foo() {
run loop@{
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@loop // 非局部的返回(non-local return), 从传递给 run 函数的 Lambda 表达式中返回
print(it)
}
}
print(" done with nested loop")
}
//sampleEnd
fun main(args: Array) {
foo()
}
当 return 语句指定了返回值时, 源代码解析器会将这样的语句优先识别为使用标签限定的 return 语句, 也就是说:
return@a 1
含义是 “返回到标签 @a 处, 返回值为 1”, 而不是 “返回一个带标签的表达式 (@a 1)”.
最后
这下应该明白为什么要用return@setOnClickListener这种写法了吧,是为了指定返回的目标位置,如果直接写return,则会直接退出onCreate方法,导致下面的代码无法执行。
うずまき ナルト.jpeg