Skip to content

等価性

Kotlinには、2種類の等価性があります:

  • _構造的_等価性 (==) - equals()関数のチェック
  • _参照_等価性 (===) - 2つの参照が同じオブジェクトを指しているかどうかのチェック

構造的等価性

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

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

anullでない場合、equals(Any?)関数を呼び出します。それ以外の場合(anullの場合)は、bが参照的にnullと等しいかどうかをチェックします:

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()関数をオーバーライドしてカスタムの等価性ロジックを提供し、このようにして構造的等価性を実装できます。

値クラス (Value classes) とデータクラス (data classes) は、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

        // 構造的等価性のためにプロパティを比較
        return this.x == other.x && this.y == other.y
    }
}

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

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

浮動小数点数の等価性

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

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

  • NaNはそれ自身と等しい
  • NaNは他のどの要素(POSITIVE_INFINITYを含む)よりも大きい
  • -0.00.0と等しくない

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

配列の等価性

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

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