※本サイトで紹介している商品・サービス等の外部リンクには、アフィリエイト広告が含まれる場合があります。

10-9. データクラス

2023年11月1日

概要

Kotlin のデータクラスは、主にデータを保持することを目的としたクラスです。
データクラスは、コンパイラによって自動的にいくつかの便利なメソッド (equals()、hashCode() 、toString( ) 、 copy( ) など) が生成されるため、データの保持と操作を簡潔に記述することができます。

書式、特徴

データクラスは、キーワード data を class の前につけて宣言します。

data class クラス名(val or var プロパティ名 : 型名 = 値, ...) {
   ...
}

データクラスの要件は以下です。

  • プライマリコンストラクタは、少なくとも1つ以上のパラメータを持つ。
  • プライマリコンストラクタのすべてのパラメータは、val か var でマークされている。
  • データクラスは、abstract, open, sealed または inner のいずれでもない。
  • データクラス自体は、public、internal、private などの可視性識別子を持つことができる。デフォルトは public
  • データは、ほかのクラスを拡張したり、インタフェースを実装したりできる。( Kotlin バージョン 1.1 より前は、インタフェースの実装しかできなかった)

コンパイラによって自動的に生成されるメソッド

データクラスを定義すると、コンパイラは自動的に以下のメソッドを生成します。
(equals, hashCode, toString については Any クラスから継承されたものがオーバライドされるというほうが正しいかもしれません。)

  • equals(other: Any?): Boolean:
    2 つのデータクラスのインスタンスが等しいかどうかを判定します。
    すべてのプロパティの値が等しい場合に true を返します。
  • hashCode(): Int:
    データクラスのインスタンスのハッシュコードを計算します。
    すべてのプロパティの値に基づいてハッシュコードが計算されます。
  • toString(): String:
    データクラスのインスタンスの文字列表現を生成します。
    すべてのプロパティの値が文字列に含まれます。
  • copy(…): データクラス名:
    データクラスのインスタンスのコピーを作成します。
    必要に応じて、プロパティの値を変更することができます。
  • componentN(): 型:
    データクラスのプロパティにアクセスするための分解宣言を提供します。
    N はプロパティの宣言順序に対応します (1 から始まります)

通常のクラスとの違い (equals, hashCode, toString の動作の違い)

通常のクラスとの違いを equals, hashCode, toString のメソッドの動作の例で比較します。

▼ 通常のクラス
同じ name と id をいれた person1 と person3 は異なると判定されたのと、それぞれの toString の結果がオブジェクトのIDになります。

class Person (val name : String, val id : Int)

fun main(){
    val person1 = Person("hogehoge", 1)
    val person2 = Person("foobar", 2)
    val person3 = Person("hogehoge", 1)

    if (person1.equals(person2)){
        println("person1 と person2 は同じ")
    } else{
        println("person1 と person2 は異なる")
    }

    if (person1 == person3){
        println("person1 と person3 は同じ")
    } else{
        println("person1 と person3 は異なる")
    }

    println("person1.hashCode(): ${person1.hashCode()}")
    println("person1.toString(): ${person1.toString()}")
    println("person2.hashCode(): ${person2.hashCode()}")
    println("person2.toString(): ${person2.toString()}")
    println("person3.hashCode(): ${person3.hashCode()}")
    println("person3.toString(): ${person3.toString()}")
}

実行結果

person1 と person2 は異なる
person1 と person3 は異なる
person1.hashCode(): 1784662007
person1.toString(): Person@6a5fc7f7
person2.hashCode(): 997110508
person2.toString(): Person@3b6eb2ec
person3.hashCode(): 509886383
person3.toString(): Person@1e643faf

▼ データクラス
1行目のクラス定義の最初に data を入れただけです。
person1 と person3 が同じであると判定されていて、hashCode も同じです。
また、toString の出力結果が、クラス名とそれぞれのパラメータ、引数で見えてきています。データクラスの場合は、これらのメソッドが クラスをインスタンス化したオブジェクトのそのものではなく、そのメンバを加味した動作となっていることが確認できます。

data class Person (val name : String, val id : Int)

fun main(){
    val person1 = Person("hogehoge", 1)
    val person2 = Person("foobar", 2)
    val person3 = Person("hogehoge", 1)

    if (person1.equals(person2)){
        println("person1 と person2 は同じ")
    } else{
        println("person1 と person2 は異なる")
    }

    if (person1 == person3){
        println("person1 と person3 は同じ")
    } else{
        println("person1 と person3 は異なる")
    }

    println("person1.hashCode(): ${person1.hashCode()}")
    println("person1.toString(): ${person1.toString()}")
    println("person2.hashCode(): ${person2.hashCode()}")
    println("person2.toString(): ${person2.toString()}")
    println("person3.hashCode(): ${person3.hashCode()}")
    println("person3.toString(): ${person3.toString()}")
}

出力結果

person1 と person2 は異なる
person1 と person3 は同じ
person1.hashCode(): 1096313719
person1.toString(): Person(name=hogehoge, id=1)
person2.hashCode(): -680542187
person2.toString(): Person(name=foobar, id=2)
person3.hashCode(): 1096313719
person3.toString(): Person(name=hogehoge, id=1)

copy メソッド

データクラスでは、copy メソッドが自動作成されます。オブジェクトのコピーを生成しますが、単なるコピーだけでなく、その引数として各プロパティの新しい値を指定することもできます。
引数は一部のみ指定も可能です。

data class Person (val name : String, val id : Int)

fun main(){
    val person1 = Person("hogehoge", 1)
    val person2 = person1.copy()
    val person3 = person1.copy(name="foobar")

    println("person1.toString(): ${person1.toString()}")
    println("person2.toString(): ${person2.toString()}")
    println("person3.toString(): ${person3.toString()}")
}

実行結果

person1.toString(): Person(name=hogehoge, id=1)
person2.toString(): Person(name=hogehoge, id=1)
person3.toString(): Person(name=foobar, id=1)

componentN メソッド

データクラスでは、componentN メソッド (N の部分は定義順の番号が入る) も自動生成されます。
componentN メソッドは、N番目のプロパティを返します。

data class Person (val name : String, val id : Int)

fun main(){
    val person1 = Person("hogehoge", 1)

    println("person1.component1(): ${person1.component1()}")
    println("person1.component2(): ${person1.component2()}")
}

実行結果

person1.component1(): hogehoge
person1.component2(): 1