跨类边界的 break 或 continue 跳转在 Kotlin 中如何实现?

16

有人遇到这个问题吗?break or continue jump across class boundary kotlin

当我使用 break 或 continue 时,出现了这个问题。在带接收者的 lambda 中,我创建了 'letIn'。

带接收者的 lambda 代码

fun letIn(componentName: String?, values: List<LifeService.Value?>?,
       body: (String, List<LifeService.Value?>) -> Unit) {
  if (!TextUtils.isEmpty(componentName) && (values != null && values.isNotEmpty())) {
     body(componentName!!, values)
  } 
}
这是它的示例代码。

这是它的示例代码。

for (option in 0 until optionsSize) {
    val component = optionsGroup?.options?.get(option)
    component?.let {
        with(component) {
            letIn(presentation, values, { componentName, values ->
                if (componentName == LifeComponentViewType.CHECKBOX) {
                    letIn(transformCheckBoxValues(optionsGroup), { data ->
                        dataSource?.push(componentName, ComponentDataCheckBoxCollection(name, data))
                        view.buildComponent(componentName)
                         // break or continue didnt work
                    })
                } else {
                    dataSource?.push(componentName, ComponentDataCollection(name, values))
                    view.buildComponent(componentName)
                }
            })
        }
    }
 }

因为上面的代码不起作用,所以我使用了命令式的方式。


for (option in 0 until optionsSize) {
val component = optionsGroup?.options?.get(option)
        if (component != null) {
            val presentation: String? = component.presentation
            val values = component.values
            if (!TextUtils.isEmpty(presentation)) {
                if (presentation == LifeComponentViewType.CHECKBOX) {
                    val data = transformCheckBoxValues(optionsGroup)
                    if (data.isNotEmpty()) {
                        dataSource?.push(presentation, ComponentDataCheckBoxCollection(optionsGroup.name, data))
                        view.buildComponent(presentation)
                        return
                    }
                } else {
                    dataSource?.push(presentation!!, ComponentDataCollection(component.name, values))
                    view.buildComponent(presentation!!)
                }
            } else {
                return
            }
        }
    }

有人有建议吗?

更新 我通过内联高阶函数来修复了这个问题。


第一个例子,第二个 letIn 只有两个参数,但被定义为需要三个。 - Les
在 Kotlin 中使用接收者的 Lambda。 - raditya gumay
你的 lambda 函数和接收 lambda 的函数都没有接收者。请参阅带接收者的函数字面值 - Les
请查看我的更新答案,其中我向您展示如何声明一个函数(和lambda)以接收器为参数。 - Les
嗨,好的。我会尝试并尽快向您提供信息。谢谢你帮助我。 - raditya gumay
请参考为什么不能在let或run中使用continue以获取解决方法,并查看KT-1436中的良好预测。 - Vadzim
1个回答

17

(除了其他编码错误以外)你看到这个错误是因为在lambda内部,你不能使用breakcontinue来跳出lambda到最近的循环。相反,你可以使用一个有资格的return来跳出lambda到标签。

参考语言参考

return表达式从最近的包围函数返回,即foo。(请注意,这种非局部返回仅支持传递给内联函数的lambda表达式。)如果我们需要从lambda表达式返回,我们必须标记并限定返回

(强调是我加的)

你的第二个例子显示你希望你的lambda表达式从包围函数中进行非局部返回。因此,你不需要限定你的return,但是你的函数letIn必须声明为inline(否则你只能进行局部、有资格的返回)。

inline fun letIn(componentName: String?, values: List<LifeService.Value?>?,
       body: (String, List<LifeService.Value?>) -> Unit) {
  if (!TextUtils.isEmpty(componentName) && (values != null && values.isNotEmpty())) {
     body(componentName!!, values)
  } 
}

... 或者如果您希望它有接收者...

inline fun String?.letIn(values: List<LifeService.Value?>?,
                         body: String.(List<LifeService.Value?>) -> Unit) {
    if (!TextUtils.isEmpty(this) && (values != null && values.isNotEmpty())) {
        this!!.body(values)
    }
}

当你将letIn声明为inline时,编译器就不会抱怨你在lambda中使用return。如果你的lambda只进行本地返回,那么你的函数不需要是inline,但它需要有一个限定符的返回(例如return@letIn)。

你的第一个示例将如下所示...

for (option in 0 until optionsSize) {
    val component = optionsGroup?.options?.get(option)
    component?.let {
        with(component) {
            presentation.letIn(values, { values ->
                if (this == LifeComponentViewType.CHECKBOX) {
                    this.letIn(transformCheckBoxValues(optionsGroup), { data ->
                        dataSource?.push(this, ComponentDataCheckBoxCollection(this, data))
                        view.buildComponent(this)
                        return //returns from function
                    })
                } else {
                    dataSource?.push(this, ComponentDataCollection(name, values))
                    view.buildComponent(this)
                    return  //returns from function
                }
            })
        }
    }
}

最后请注意,如果您想要提前跳出 lambda 表达式,但是继续外部循环,可以使用以下方式:

fun test1() {
    val list = listOf("a", "b", "c")
    val optionsSize = 2
    for(i in 0..optionsSize) loop@ {
        println("calliing list.forEach")
        list.forEach lit@ {
            if(it == "a") return@lit
            if(it == "c") return@loop
            println(it)
        }
    }
}

这样行不通。虽然 Intelli-sense 没有抱怨,但编译器会报告一个内部错误。但你可以将外层循环转换为 Lambda 表达式,这样就可以正常工作……

fun test() {
    val list = listOf("a", "b", "c")
    val optionsSize = 2
    (0..optionsSize).forEach() loop@ {
        println("calliing list.forEach")
        list.forEach lit@ {
            if(it == "a") return@lit
            if(it == "c") return@loop
            println(it)
        }
    }
}

需要强调的是,仅当传递给lambda函数的函数被声明为inline(像forEach被声明为inline一样),此方法才有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接