为什么对列表的更改也会改变 ViewModel 中的另一个列表?

问题描述 投票:0回答:1

我有一个ArrayList

val tasks = remember { mutableStateListOf<TaskItem>() }

任务项在哪里

@Keep
@Parcelize
data class Task(
    var task: String,
    var done: Boolean = false
) : Parcelable

@Keep
@Parcelize
data class TaskItem(var id: String, var task: Task) : Parcelable

当我进入目标屏幕时,将一些数据传递到

tasks

LaunchedEffect(key1 = Unit) {
    tasks.clear()
    tasks.addAll(data.tasks)
    Log.wtf("TST_data.tasks", data.tasks.map(TaskItem::task).toString())
    // Logs: TST_data.tasks     [Task(task=Broccoli 🥦, done=true)]
}

其中,

data
由 DataModel 类驱动:

class DataModel(private val application: Application) : AndroidViewModel(application) {
    ....
    val tasks = mutableStateListOf<TaskItem>()
    ....
}

到目前为止一切都很好,但是在对

tasks
进行了一些更改之后,假设我将
done
的值更改为
false
并且我想在离开屏幕之前保存它。为了确保有一些变化,我比较了这样的列表:

if(data.tasks.map(TaskItem::task).toList() != tasks.map(TaskItem::task).toList())
    // proceed with saving
else
    // a toast massage: No changes made.

问题是,它进入了

else
块,这意味着尽管我做了更改,但没有任何更改。在调用
if
语句之前,我尝试记录这些列表以查看元素:

Log.wtf("TST_data.tasks", data.tasks.map(TaskItem::task).toString())
Log.wtf("TST_tasks", tasks.map(TaskItem::task).toString())

结果如下:

TST_data.tasks [Task(task=Broccoli 🥦, done=false)]
TST_tasks     [Task(task=Broccoli 🥦, done=false)]

tasks
内的改变值也改变了
data.tasks
内的值,这是不可能的,因为我确实注意到改变了
data.tasks
内的任何内容。

如您所见,每当我更改

data.tasks
时,
tasks
都会发生变化。

这应该是不可能的!为什么会发生这种情况以及如何防止这种变化?

kotlin arraylist android-jetpack-compose android-viewmodel data-class
1个回答
0
投票

如果将相同的项目(对 DataModel 实例的引用)放入两个不同的列表中,则两个列表都指向同一个实例。如果您通过更改该实例的

var
属性之一来修改该实例,则两个列表都会看到相同的更改,因为它们正在查看同一实例。

因此,如果不执行所谓的深层复制,就无法比较数据的新旧版本。创建新列表时复制列表中的每个单独实例看起来像这样:

list.map { it.copy() }

当然,您必须在更改列表中的项目之前执行此操作。如果模型类中的任何项 also 具有

var
属性,事情就会变得更加复杂。这非常容易出错,这就是为什么建议不要在任何模型类中使用任何
var
属性。它们应该是不可变的类。当您需要更改某些内容时,您可以将列表中的项目替换为应用了更改的副本。这就是为什么 Kotlin 数据类提供了方便的
copy()
函数,允许您在进行复制时更改特定属性值。

除了容易出错之外,它还出于同样的原因破坏了 Compose 的状态功能。状态无法检测来自变异类的更改,因为当它们发生变异时,有关先前状态的信息就会丢失,并且无法进行比较以检测更改。

© www.soinside.com 2019 - 2024. All rights reserved.