1. 概要

このチュートリアルでは、Kotlinでのオブジェクト比較のさまざまな方法を探ります。

=== とそれに対応する!== は、参照IDに使用される二項演算子です。 これは、2つの参照が同じオブジェクトを指している場合、結果が真になることを意味します。 ただし、ほとんどの場合、2つのオブジェクトが同等の値を持っているかどうかを確認する必要があります。

それでは、Kotlinの構造的平等のさまざまな方法を調査してみましょう。

2. ==演算子を使用した等式

==とその反対の!=は、2つのオブジェクトを比較するために使用されます。2つの文字列を== と比較すると、目的の結果が得られます。

val a = "Baeldung"
val b = "Baeldung"
assertTrue(a == b)

しかし、それは必ずしも私たちが望むものではないかもしれません。 次のようなクラスがあるとします。

class Storage(private val name: String, private val capacity: Int)

次に、このクラスの2つのインスタンスを比較してみましょう。

val storage1 = Storage("980Pro M.2 NVMe", 1024)
val storage2 = Storage("980Pro M.2 NVMe", 1024)
assertFalse(storage1 == storage2)

結果は誤りです! しかし、これはどのように可能ですか? その理由は文字列プールです。 文字列オブジェクトを宣言すると、ランタイムは文字列プールで同じ値をチェックします。 一致するものが見つかると、文字列リテラルがプールで見つかった文字列オブジェクトの参照に置き換えられます。 そのため、「Baeldung」は「Baeldung」と同じです。 定数プールの考え方は、数値、文字、ブール値にも当てはまります。

ただし、 Storage クラスでは、storage1storage2が等しいかどうかを明示的に指定する必要があります。 それ以外の場合は、IDチェックが行われます。 つまり、参照を照合することによって同等性をチェックします。 その場合、 storage1 == storage2falseを返します。

3. equals()および hashCode()をオーバーライドする

== 演算子を使用するたびに、内部で equals()関数が呼び出されます。 そして、前述のように、IDチェックが行われます。 equals()をオーバーライドして、カスタムの等価性チェックの実装を提供できます:

class StorageOverriddenEquals(val name: String, val capacity: Int) {
    override fun equals(other: Any?): Boolean {
        if (other == null) return false
        if (this === other) return true
        if (other !is StorageOverriddenEquals) return false
        if (name != other.name || capacity != other.capacity) return false
        return true
    }
}

これで、 equals()の答えは true であり、私たちにとって意味があります。

val storage1 = StorageOverriddenEquals("980Pro M.2 NVMe", 1024)
val storage2 = StorageOverriddenEquals("980Pro M.2 NVMe", 1024)
assertTrue(storage1 == storage2)

重要な点は、equals()メソッドとhashCode()メソッドの両方を常に一緒にオーバーライドする必要があるということです。理由はハッシュコードコントラクトです。

ルールを尊重しないと、Setsなどのハッシュベースのコレクションが正しく機能しません。これを修正するには、 hashCode()関数をオーバーライドできます。

class StorageOverriddenEqualsAndHashCode(private val name: String, private val capacity: Int) {
    override fun equals(other: Any?): Boolean {
        if (other == null) return false
        if (this === other) return true
        if (other !is StorageOverriddenEqualsAndHashCode) return false
        other as StorageOverriddenEqualsAndHashCode
        if (name != other.name || capacity != other.capacity) return false
        return true
    }
    override fun hashCode(): Int = name.hashCode() * 31 + capacity
}

4. データクラスの使用

または、データクラスを使用して、オブジェクトを比較するために必要な定型文を回避できます。

data class StorageDataClass(private val name: String, private val capacity: Int)
val storage1 = StorageDataClass("980Pro M.2 NVMe", 1024)
val storage2 = StorageDataClass("980Pro M.2 NVMe", 1024)
assertTrue(storage1 == storage2)

この比較の結果は真実です。

5. 結論

この記事では、==演算子のデフォルトの動作をオーバーライドする方法を説明しました。

すべての例は、GitHubから入手できます。