编程Kotlin 开发人员

Kotlin 中的 Data Objects (数据对象) 是如何工作的,它们的用途是什么,equals/hashCode/toString 是如何实现的,以及它们与普通的 object 和 data class 有何不同?

用 Hintsage AI 助手通过面试

回答。

问题的历史:

在 Kotlin 1.9 之前,object 对象不能是数据对象 — 你不能拥有一个单例,自动获取 equals, hashCode, toString 方法,就像数据类那样。随着 data object 的出现,这一限制被取消了。现在可以创建一个带有自动生成方法的单例对象,适用于值和类枚举样式的模式。

问题:

以前,为了获得正确的 equals(), hashCode(), toString(),即使是单例对象,也需要手动实现它们或使用其他技巧,这增加了冗余代码和出错的可能性。

解决方案:

使用 data object 来表示那些实例唯一的对象,同时需要标准的 equals/hashCode/toString 行为,以便于在集合中传递、序列化、比较和调试。

代码示例:

data object NotAvailable fun checkStatus(status: Any) = when (status) { NotAvailable -> "数据缺失" else -> "其他状态" } val set = setOf(NotAvailable) println(NotAvailable in set) // true println(NotAvailable.toString()) // NotAvailable

关键特性:

  • Data object 根据数据类的契约实现 equals/hashCode/toString
  • 它是一个单例对象(唯一实例)
  • 适用于没有数据的值类或枚举类模式

有陷阱的问题。

data object 可以包含属性吗?

可以,但只能是没有 backing field 的 val 属性(因为单例不应存储任何东西)

data object Loading { val status: String get() = "加载中..." }

从 equals 的角度看,data object 与普通的 object 有什么不同?

普通对象的 equals 只检查引用相等性,而 data object 则比较数据契约,但在单例情况下,这永远是同一个对象。然而,重写的 equals/hashCode 对于集合来说更有用。

可以从 data object 继承吗?

不可以,data object 是最终的,就像 Kotlin 中的任何对象一样 — 不能继承。

常见错误和反模式

  • 使用 data object 代替 enum,当对象数量较多时
  • 使用 data object 试图存储数据 — 不允许
  • 不考虑相等性始终是引用类型,但预计通过值来处理集合

生活中的例子

负面案例

为了所有状态使用不同的 data object,而不是 enum。经过一年,需求串行化成字符串,需要手动将对象名称与类型进行匹配。

优点:

  • 易于初始化

缺点:

  • 多余的串行化错误,匹配错误

正面案例

对于网络请求的返回值,使用 data object 表示特定状态:Loading、Empty、Error。这样代码紧凑,支持 equals, hashCode, toString 自动。

优点:

  • 方便在集合中检查
  • 美观的日志记录

缺点:

  • 不能添加可变属性,仅限 val-lazy