5.10 相等性

原文链接: https://kotlinlang.org/docs/equality.html

5.10 相等性

在 Kotlin 中,有两种相等性:

  • 结构相等性(==)—— 对 equals() 函数的检查
  • 引用相等性(===)—— 检查两个引用是否指向同一个对象

结构相等性

结构相等性用于验证两个对象是否具有相同的内容或结构。结构相等性由 == 运算及其取反形式 != 检查。按照约定,像 a == b 这样的表达式会被转换为:

1
a?.equals(b) ?: (b === null)

如果 a 不为 null,就调用 equals(Any?) 函数。否则(a 为 null),则检查 b 是否在引用上等于 null:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
fun main() {
    var a = "hello"
    var b = "hello"
    var c = null
    var d = null
    var e = d

    println(a == b)
    // true
    println(a == c)
    // false
    println(c == e)
    // true
}

注意,与 null 显式比较时没有必要优化代码:a == null 会被自动转换为 a === null。

在 Kotlin 中,所有类都从 Any 类继承 equals() 函数。默认情况下,equals() 函数实现的是引用相等性。不过,Kotlin 中的类可以重写 equals() 函数来提供自定义的相等性逻辑,从而实现结构相等性。

值类和数据类是两种会自动重写 equals() 函数的特殊 Kotlin 类型,因此它们默认实现结构相等性。

不过,对于数据类,如果 equals() 函数在父类中被标记为 final,它的行为就保持不变。

不同的是,非数据类(未用 data 修饰符声明的类)默认不会重写 equals() 函数。非数据类实现的是从 Any 类继承的引用相等性行为。要实现结构相等性,非数据类需要自定义相等性逻辑来重写 equals() 函数。

要提供自定义的相等性检查实现,请重写 equals(other: Any?): Boolean 函数:

1
2
3
4
5
6
7
8
9
class Point(val x: Int, val y: Int) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is Point) return false

        // 比较属性以实现结构相等性
        return this.x == other.x && this.y == other.y
    }
}

注意: 重写 equals() 函数时,你也应当重写 hashCode() 函数,以保持相等性与哈希的一致性,并确保这些函数行为正确。

同名但签名不同的函数(例如 equals(other: Foo))不会影响 == 和 != 运算符的相等性检查。

结构相等性与 Comparable<...> 接口定义的比较无关,因此只有自定义的 equals(Any?) 实现才会影响该运算符的行为。

引用相等性

引用相等性检查两个对象的内存地址,以判断它们是否是同一个实例。

引用相等性由 === 运算及其取反形式 !== 检查。当且仅当 a 和 b 指向同一个对象时,a === b 的结果为 true:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
fun main() {
    var a = "Hello"
    var b = a
    var c = "world"
    var d = "world"

    println(a === b)
    // true
    println(a === c)
    // false
    println(c === d)
    // true

}

对于运行时由基本类型表示的值(例如 Int),=== 相等性检查等价于 == 检查。

提示: 引用相等性在 Kotlin/JS 中的实现方式不同。关于相等性的更多信息,请参阅 Kotlin/JS 文档。

浮点数相等性

当相等性检查的操作数在静态类型上已知为 Float 或 Double(无论是否可空)时,该检查遵循 IEEE 754 浮点运算标准。

对于静态类型不是浮点数的操作数,行为有所不同。在这些情况下,实现的是结构相等性。因此,操作数静态类型不是浮点数的检查与 IEEE 标准存在差异。在这种场景下:

  • NaN 等于其自身
  • NaN 大于任何其他元素(包括 POSITIVE_INFINITY)
  • -0.0 不等于 0.0

更多信息请参阅浮点数比较。

数组相等性

要比较两个数组是否包含顺序相同的相同元素,请使用 contentEquals()。

更多信息请参阅比较数组。