将isSelected移到数据类构造体外后StateFlow不再发射事件原因
问题:将data class属性移到构造体外后StateFlow不再发射事件
之前定义了如下Student数据类,配合ViewModel和Fragment使用时,点击按钮能正常触发StateFlow发射事件,日志输出isSelected的变化:
data class Student( val name: String, var isSelected: Boolean = false )
ViewModel代码:
class FirstViewModel : ViewModel() { private val _student = MutableStateFlow(Student("Sam")) val student = _student.asStateFlow() fun updateStudentOnSelectionChange(student: Student) { val newStudent = student.copy().apply { isSelected = !isSelected } _student.update { newStudent } } }
Fragment代码:
class FirstFragment : Fragment() { private lateinit var binding: FragmentFirstBinding private lateinit var viewModel: FirstViewModel override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { binding = DataBindingUtil.inflate(inflater, R.layout.fragment_first, container, false) viewModel = ViewModelProvider(this)[FirstViewModel::class.java] binding.updateButton.setOnClickListener { viewModel.updateStudentOnSelectionChange(viewModel.student.value) } viewModel.student.collectOnStarted(viewLifecycleOwner) { Log.v("xxx", "isSelected: ${it.isSelected}") } return binding.root } }
但将isSelected移到构造体外作为类体属性后:
data class Student( val name: String ) { var isSelected: Boolean = false }
点击按钮后StateFlow不再发射事件,原因如下:
核心原因分析
- Data类的
copy()方法只处理主构造属性:Kotlin的data类自动生成的copy()方法,只会复制主构造函数中声明的属性,类体里定义的属性不会被复制,而是在新对象初始化时重新执行类体中的赋值逻辑(这里就是isSelected = false)。 - StateFlow的更新依赖相等性判断:
MutableStateFlow.update()方法会先对比新值和当前旧值的相等性(通过equals()方法),只有当两者不相等时才会发射新值。 - Data类的
equals()默认只比较主构造属性:data类自动生成的equals()和hashCode()方法,仅基于主构造函数中的属性进行比较。修改后的Student类,主构造只有name属性,所以只要两个Student对象的name相同,equals()就会返回true。
结合你的代码来看:
- 修改后调用
student.copy()得到的新对象,name和原对象一致,equals()返回true。 - 即使你在
apply中修改了isSelected,但这个属性不参与equals()比较,StateFlow会认为新值和旧值没有变化,因此不会发射事件。
解决办法
- 推荐:将
isSelected放回主构造函数:这是最符合data类设计初衷的做法,所有需要参与数据相等性判断的属性都应该放在主构造中,这样copy()和equals()都会正确处理该属性,StateFlow的更新逻辑也会正常工作。 - 可选:重写
equals()和hashCode():如果一定要把isSelected放在类体中,需要手动重写这两个方法,将isSelected纳入比较范围。但这会失去data类自动生成代码的便利性,不推荐。
内容的提问来源于stack exchange,提问作者Sam Chen
相关产品推荐
相关产品推荐

