Skip to content

等価性

Kotlinでは、2種類の等価性があります。

  • 構造的等価性 (==) - equals()関数のチェック
  • 参照等価性 (===) - 同じオブジェクトを指す2つの参照のチェック

構造的等価性

構造的等価性は、2つのオブジェクトが同じ内容または構造を持つかどうかを検証します。構造的等価性は、==演算子とその否定形である!=によってチェックされます。 慣例により、a == bのような式は次のように変換されます。

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

anullでない場合、equals(Any?)関数を呼び出します。それ以外の場合(anullの場合)、bnullと参照等価であるかを確認します。

kotlin
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では、equals()関数はすべてのクラスがAnyクラスから継承します。デフォルトでは、equals()関数は参照等価性を実装します。しかし、Kotlinのクラスはequals()関数をオーバーライドして、カスタムの等価性ロジックを提供し、その方法で構造的等価性を実装できます。

値クラスとデータクラスは、equals()関数を自動的にオーバーライドする2つの特定のKotlin型です。そのため、それらはデフォルトで構造的等価性を実装します。

ただし、データクラスの場合、親クラスでequals()関数がfinalとマークされている場合、その動作は変更されません。

対照的に、非データクラス(data修飾子で宣言されていないクラス)は、デフォルトではequals()関数をオーバーライドしません。代わりに、非データクラスはAnyクラスから継承された参照等価性の動作を実装します。構造的等価性を実装するには、非データクラスはequals()関数をオーバーライドするためのカスタムの等価性ロジックを必要とします。

カスタムの等価性チェック実装を提供するには、equals(other: Any?): Boolean関数をオーバーライドします。

kotlin
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

        // Compares properties for structural equality
        return this.x == other.x && this.y == other.y
    }
}

NOTE

equals()関数をオーバーライドする場合、等価性とハッシュ処理の間の一貫性を保ち、これらの関数が適切に動作するように、hashCode()関数もオーバーライドする必要があります。

同じ名前で異なるシグネチャを持つ関数(equals(other: Foo)など)は、==および!=演算子による等価性チェックに影響を与えません。

構造的等価性は、Comparable<...>インターフェースによって定義される比較とは関係ありません。そのため、カスタムのequals(Any?)実装のみが演算子の動作に影響を与える可能性があります。

参照等価性

参照等価性は、2つのオブジェクトのメモリアドレスを検証し、それらが同じインスタンスであるかどうかを判断します。

参照等価性は、===演算子とその否定形である!==によってチェックされます。a === bは、abが同じオブジェクトを指す場合にのみtrueと評価されます。

kotlin
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)で表現される値の場合、===等価性チェックは==チェックと同等です。

TIP

Kotlin/JSでは、参照等価性の実装が異なります。等価性の詳細については、Kotlin/JSドキュメントを参照してください。

浮動小数点数の等価性

等価性チェックのオペランドが静的にFloatまたはDouble(null許容か否かにかかわらず)であることが判明している場合、そのチェックはIEEE 754 浮動小数点数算術標準に従います。

静的に浮動小数点数として型付けされていないオペランドの場合、動作は異なります。これらの場合、構造的等価性が実装されます。結果として、静的に浮動小数点数として型付けされていないオペランドによるチェックは、IEEE標準とは異なります。このシナリオでは、

  • NaNはそれ自身と等価です。
  • NaNは他のどの要素(POSITIVE_INFINITYを含む)よりも大きいです。
  • -0.00.0とは等しくありません。

詳細については、浮動小数点数の比較を参照してください。

配列の等価性

2つの配列が同じ順序で同じ要素を持つかどうかを比較するには、contentEquals()を使用します。

詳細については、配列の比較を参照してください。