Kotlin データクラス

データクラス(data class)

データクラス(Data Class)は、データを保存するためのクラスである。データクラスはclassキーワードの前にdataを付けて宣言する。 データクラスとして宣言すると、toString()hashCode()equals()copy()関数が自動生成され、通常のクラスとほぼ同じように使用される。

class Foo1(var name: String)        // 通常のクラス
data class Foo2(var name: String)   // データクラス

fun main() {
    var a1 = Foo1("devkuma")
    println(a1)              // Foo1@2280cdac

    var a2 = Foo2("devkuma")
    println(a2)              // Foo2(name=devkuma)

    var a3 = a2.copy()
    println(a3)              // Foo2(name=devkuma)
}

データクラス(Data class)の特徴

  • 継承できない。
  • コンストラクタプロパティはvalまたはvarで宣言する必要がある。
  • コンストラクタには1つ以上のプロパティを宣言する必要がある。
  • abstractopensealedinnerを付けられない。
  • toString()hashCode()equals()copy()をOverride実装した場合は、その実装関数が使用される。

自動生成される関数

次の例では、以下のような通常クラスとデータクラスを作成して進める。

通常クラス:

class PersonGeneral(var name: String, val age: Int)

データクラス:

data class PersonData(var name: String, val age: Int)

toString()

toString()関数は、コンストラクタに定義されたプロパティの値を返す。

fun main() {
    var personGeneral = PersonGeneral("devkuma", 21)
    var personData = PersonData("devkuma", 21)

    println(personGeneral)
    println(personData)
}

Output:

basic.clazz.data.PersonGeneral@548c4f57
PersonData(name=devkuma, age=21)
  • 通常クラスのtoString()ではオブジェクトの情報が返された。
  • データクラスのtoString()ではプロパティと値が返されている。

hashCode()

hashCode()関数は、通常のclassオブジェクトであれば異なる値を返すが、data classの場合は同じ値を返す。

次の例では、通常クラスとデータクラスをそれぞれ2つずつ、合計4つ作成してhashCode()値を取得している。

fun main() {
    var personGeneral1 = PersonGeneral("devkuma", 21)
    var personGeneral2 = PersonGeneral("devkuma", 21)
    
    var personData1 = PersonData("devkuma", 21)
    var personData2 = PersonData("devkuma", 21)

    println(personGeneral1.hashCode())
    println(personGeneral2.hashCode())

    println(personData1.hashCode())
    println(personData2.hashCode())
}

Output:

1418481495
303563356
1111582818
1111582818
  • personGeneral1.hashCode()personGeneral2.hashCode()の戻り値は異なる値になった。
  • personData1.hashCode()personData2.hashCode()の戻り値は同じ値になった。

equals()

equals()を見る前に、Kotlinの比較演算子について確認する。JavaとKotlinでは、値の比較とメモリ上の比較に違いがある。

チェック対象 Java Kotlin
値が同じかチェック .equals() == OR .equals()
メモリ上で同じオブジェクトかチェック == ===

次の例では、data classに同じ値を入れた2つのクラスを比較している。

fun main() {
    var personData1 = PersonData("devkuma", 21)
    var personData2 = PersonData("devkuma", 21)
    
    println(personData1 == personData2)
    println(personData1 === personData2)
}

Output:

true
false
  • ==を使用した場合は値が同じかをチェックしているため、trueが返された。
  • ===を使用した場合はメモリ上で同じオブジェクトかをチェックしているため、falseが返された。

copy()

copy()関数は、コンストラクタに定義されたプロパティに限って値を変更してコピーできる。

次の例では、データクラスをまず1つ作り、2つのクラスにcopy()関数を使ってプロパティ値をコピーしている。

fun main() {
    val personData1 = PersonData("devkuma", 21)

    val personData2 = personData1.copy()
    val personData3 = personData1.copy(name = "kimkc")

    println(personData1)
    println(personData2)
    println(personData3)
}

Output:

PersonData(name=devkuma, age=21)
PersonData(name=devkuma, age=21)
PersonData(name=kimkc, age=21)
  • 1つ目のオブジェクトにcopy()関数でプロパティの値をコピーした。
  • 2つ目のオブジェクトではcopy(name = "kimkc")関数でnameの値だけを変更してプロパティの値をコピーした。

分解宣言(Destructuring Declarations)

通常、オブジェクトから変数値を取得するには次のようにするだろう。

fun main() {
    val personData = PersonData("devkuma", 21)

    val name = personData.name
    val age = personData.age

    println("$name, $age years of age")
}

Output:

devkuma, 21 years of age

Kotlinでは分解宣言(Destructuring Declarations)をサポートしているため、次のように短く書ける。

fun main() {
    val personData1 = PersonData("devkuma", 21)

    val (name, age) = personData1

    println("$name, $age years of age")
}

Output:

devkuma, 21 years of age
  • val (name, age) = personData1のコードのように、データクラスの値がそれぞれの変数に代入される。

参考