Edit Page

データクラス

Kotlinのデータクラスは、主な目的がデータの保持であるようなクラスの事です。 データクラスはインスタンスを人間に読めるような形式で出力したり、 インスタンスを比較したり、インスタンスをコピーしたり、そのほか様々なメンバ関数が自動でついてきます。

データクラスはdataでマークされます:

data class User(val name: String, val age: Int)

コンパイラは自動的に、プライマリコンストラクタで宣言されたすべてのプロパティから、次のメンバを派生します:

  • equals() / hashCode() のペア、
  • "User(name=John, age=42)" 形式の toString()
  • 宣言した順番でプロパティに対応する componentN() 関数
  • copy() 関数(下記参照)。

生成されたコードの一貫性と意味のある動作を保証するために、 データクラスは、次の要件を満たさなければなりません:

  • プライマリコンストラクタは、少なくとも1つのパラメータを持っている必要があります。
  • すべてのプライマリコンストラクタのパラメータは、 val または var としてマークする必要があります。
  • データクラスは、 abstract, open, sealed または inner にすることはできません。

さらに、メンバの継承に関連して、生成されるデータクラスのメンバは以下のルールに従います:

  • .equals(), .hashCode(), .toString()の明示的な実装がデータクラスの本体にあるか、基底クラスにfinalの実装があれば、これらの関数は生成されず、すでにある実装が使われます。
  • 基底型が.componentN()openで定義してあって戻りの型が互換性があれば、生成される対応する関数は基底クラスのoverrideとして実装されます。また、シグニチャが互換性が無かったりfinalだたりしてoverride出来ない時は、エラーとして報告されます。
  • .componentN().copy()を明示的に実装する事は禁止されています。

データクラスは他のクラスを継承する事が出来ます(sealedクラスに例があります)。

JVM上で、生成されたクラスがパラメータなしのコンストラクタを持つ必要がある場合は、 すべてのプロパティのデフォルト値を指定する必要があります(コンストラクタを参照してください)。

data class User(val name: String = "", val age: Int = 0)

クラス本体に宣言されたプロパティ

コンパイラが自動生成に使うのはプライマリコンストラクタに含まれているプロパティだけです。 だからあるプロパティを自動生成から除外したければ、 クラスの本体に実装すればいいでしょう:

data class Person(val name: String) {
    var age: Int = 0
}

この例では、nameプロパティだけが .toString().equals().hashCode().copy()の実装で使われて、 コンポーネント関数も.component1()だけです。 ageプロパティが.toString().equals().hashCode().copy()の実装で使われない理由は、 ageプロパティがクラス本体に宣言されているからです。 もし二つのPersonオブジェクトが異なるage(訳注:年齢)でありながらnameが同じなら、 両者は等価として扱われます。 これは.equals関数がnameの等価(equality)チェックしかしないからです。 例えば:

data class Person(val name: String) { var age: Int = 0 } fun main() { //sampleStart val person1 = Person("John") val person2 = Person("John") person1.age = 10 person2.age = 20 println("person1 == person2: ${person1 == person2}") // person1 == person2: true println("person1は年齢 ${person1.age}: ${person1}") // person1は年齢 10: Person(name=John) println("person2は年齢 ${person2.age}: ${person2}") // person2は年齢 20: Person(name=John) //sampleEnd }

コピー

オブジェクトをコピーするには.copy()関数が使えます。 プロパティの いくつか を変更し、残りをそのままにしてオブジェクトをコピーする、という事も出来ます。 User クラスの場合、その実装は次のようになります。

fun copy(name: String = this.name, age: Int = this.age) = User(name, age)     

これを用いて、次のように書くことができます:

val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)

データクラスと分解宣言 (Destructuring Declarations)

データクラスのために生成された コンポーネント関数 は、分解宣言(destructuring declaration)で使用できます。

val jane = User("Jane", 35)
val (name, age) = jane
println("$name, 年齢は $age 歳") 
// Jane, 年齢は 35 歳

標準データクラス

標準ライブラリは、 PairTriple を提供します。 ですがほとんどの場合、プロパティに意味のある名前を提供することによりコードが読みやすくなるため、 データクラスを使う方がより良い設計上の選択です。