将挂起函数包装在Lambda中传递时遇Kotlin报错问题咨询
我定义了如下数据类:
data class ReportItem(val label: String, val query: () -> Unit) { override fun toString() = label }
尝试创建该类的列表时:
val reports = listOf(ReportItem(resources.getString(R.string.sales)) { RoomDB.getInstance(requireContext()).saleItems().getBySales() })
出现了报错:
Suspend function '@Query(...) suspend fun getBySales(): List' should be called only from a coroutine or another suspend function.
我明白报错的意思,但觉得这个报错不对——我并没有在这里调用getBySales,只是把相关逻辑放在lambda里,打算后续在协程内调用ReportItem的query函数时才让getBySales实际执行。想问这是Kotlin编译器识别不了未调用的挂起函数,还是我的代码本身有问题?
这不是Kotlin的缺陷,是你的代码逻辑有问题:你在lambda里直接调用了挂起函数getBySales(),而不是只传递函数引用或者延迟执行的逻辑。
仔细看你的lambda代码:{ RoomDB.getInstance(requireContext()).saleItems().getBySales() },这里的getBySales()是实实在在写了调用语句的——虽然这个lambda现在还没执行,但Kotlin编译器会检查lambda内部的代码合法性。你的query类型是普通的() -> Unit,不是挂起函数类型suspend () -> Unit,所以lambda属于非协程环境,在里面调用挂起函数自然会触发报错。
解决办法很简单:
- 把
ReportItem的query参数改成挂起函数类型:
data class ReportItem(val label: String, val query: suspend () -> Unit) { override fun toString() = label }
这样lambda内部调用挂起函数就合法了,后续你在协程里调用query时也完全符合要求,和你原本的预期一致。
如果实在不想修改ReportItem的定义,也可以在lambda内部用协程包裹挂起函数调用(比如用GlobalScope.launch,但要注意上下文管理),不过这种方式不如直接定义挂起函数类型清晰规范,更推荐第一种方案。
内容的提问来源于stack exchange,提问作者user1785730

