问题描述
我最近开始使用 Kotlin,现在正在用一些基本的东西进行测试。想象一下,我有一个用于访问数据库的类,让它看起来像这样:
@ThreadLocal object DataService {
private var dao: DataAccessObject? = null
private val context = dispatchers.Default
fun injectDao(dao: DataAccessObject) {
Coroutinescope(context).async {
DataService.dao = dao
}
}
suspend fun get(dataObjectType: TypeOfDataObject,id: String): DataObject? = withContext(context) {
dao?.get(dataObjectType = dataObjectType,id = id)
}
}
它的设计方式是无论何时何地我们调用它的线程 - 代码都将在后台运行。为了实现这一点,每当我们注入 dao
依赖项时,它都会被注入到 DataService
对象的后台线程副本中。基本上它工作得很好,并且一切都是在后台触发的。但是当我们例如使用 get
函数然后尝试修改返回的对象时 - 我们有一个可变性异常。所以这段代码不起作用:
suspend fun test() {
var object = get(TypeOfDataObject.someObject,id = "F702BCAF-DD63-4196-AA7B-E214248CAEB7")
object.id = "whatever"
}
那么处理这种情况的最佳方法是什么?想象一下,我只想从 db 获取一个元素,更改一些值并将其写回,然后传递给 update
函数。我怎样才能做到这一点?
Kotlin's Immutability 是否意味着如果这个对象是在后台线程中创建的,它也只能在后台线程中更新?
解决方法
Kotlin 的不变性是否意味着如果这个对象是在后台线程中创建的,它也只能在后台线程中更新?
总而言之,是的。如果你想要可变状态,简单的答案就是在你创建它的线程中改变它。
withContext(context) {
//whatever
}
从传递给 withContext
的 lambda 返回的任何内容都将在返回时冻结。
现在,我觉得我没有在做我的工作,因为我没有指出你可能不应该首先改变数据对象。但是,您并不是在寻求该建议,因此我们将不理会这个话题。
我会做这样的事情:
suspend fun get(dataObjectType: TypeOfDataObject,id: String,doStuff:(DataObject?)->DataObject?): DataObject? = withContext(context) {
doStuff(dao?.get(dataObjectType = dataObjectType,id = id))
}
然后这样称呼它:
suspend fun test() {
var object = get(TypeOfDataObject.someObject,id = "F702BCAF-DD63-4196-AA7B-E214248CAEB7") { newObject ->
newObject.id = "whatever"
}
}
,
我找到了适合我的解决方案。我会在下面留下一些笔记,以供将来可能遇到此问题的时间旅行者使用。
基本上,我使我试图完成的任务过于复杂。在这种特殊情况下,使 aggregate(mpg ~ am,data = mtcars,mean)
> aggregate(mpg ~ am,mean)
am mpg
1 0 17.14737
2 1 24.39231
函数按我预期工作的最简单和最合适的方法就是不使用它们。
我更喜欢反应式编程风格,因此对于 Kotlin 世界来说,使用简单的闭包或 lambda 对我来说没有什么坏处。所以看起来很简单:
suspend
感谢 Kevin,他解释了这些基础知识,帮助我找到了一种简单而合适的方法。
附言如果有任何时间旅行者会阅读本文,请发表评论,@ThreadLocal object DataService {
private var dao: DataAccessObject? = null
private val scope = GlobalScope
fun injectDao(dao: DataAccessObject) {
scope.async {
DataService.dao = dao
}
}
fun get(dataObjectType: KNTypeOfDataObject,onComplete: (KNDataObject?) -> Unit) {
scope.async {
dao?.get(dataObjectType = dataObjectType,id = id,onComplete = onComplete)
}
}
}
函数在哪些情况下真正适合我使用反应式编程风格?