dataに関する変更点
次に紹介するのは、dataに関する変更点です。
dataクラスとは
dataクラスはKotlinの特徴的なクラスで、例えばリスト6のようなクラス宣言です。
data class PersonalData( // (1) val name: String, // (2) val email: String // (2) )
リスト6の(1)のように、クラス宣言にdataキーワードを利用します。そして、プライマリコンストラクタの引数として(2)のようにプロパティを定義します。(2)ではval宣言としていますが、もちろんvarも利用できます。
このように、プロパティ以外は定義していないクラスですが、dataクラスとして作成された時点で、表1のメソッドが自動生成されています。
メソッド名 | 内容 |
equals() | 保持しているデータ内容が同じかどうかを判定するメソッド |
hashCode() | 保持しているデータを元にしたハッシュコードを取得するメソッド |
toString() | 保持しているデータの表示に適した文字列を生成してくれるメソッド |
componentN() | 各プロパティの値を取得できるメソッド |
copy() | 同じデータを持つ別のインスタンスを生成するメソッド |
例えば、リスト6のPersonalDataを利用してリスト7のようなコードを実行したとします。
val taro = PersonalData("田中太郎", "taro@tanaka.com") println(taro)
すると、「PersonalData(name=田中太郎, email=taro@tanaka.com)」のように表示されます。単なるクラスのインスタンスをそのまま表示させた場合は「PersonalData@3af49f1c」という表記になるので、比べるとtoString()が表示に最適化されているのがわかります。
なお、通常のクラス同様に、任意のメソッドを追加で定義することもできます。
スーパークラスを持てるように変更
このdataクラスに関して、バージョン1.1で任意のクラスを継承できるように変更されています。例えば、リスト8のような抽象クラスPersonalBaseがあるとします。
abstract class PersonalBase { abstract fun show() }
リスト8のPersonalDataを、このPersonalBaseクラスを継承したdataクラスとした場合、PersonalBaseクラスに定義されている抽象メソッドshow()をオーバーライドして、リスト9のように作成します。
data class PersonalData( : ) : PersonalBase() { override fun show() { println("${name}のメールアドレスは${email}です。") } }
show()メソッドが定義されていますが、dataクラスなので表1のメソッドも自動的に生成されています。
dataオブジェクトが利用可能に
いくつかのメソッドが自動生成されたdataクラスをもっと手軽に利用できるように、バージョン1.9でdataオブジェクトが導入されました。
これは、例えばリスト10の(1)のようなコードです。単にobject宣言にdataをつけるだけです。
data object Yamamoto { // (1) val name = "山本三郎" // (1) } // (1) println(Yamamoto) // (2)
すると自動的にtoString()メソッドが生成され、(2)の実行結果として「Yamamoto@31befd9f」のような表示にはならず、「Yamamoto」のようにオブジェクト名が表示されるようになります。
ただし、componentN()とcopy()メソッドは自動生成されていないので注意してください。
dataとsealedの組み合わせ
dataクラスは、sealedクラスやsealedインターフェースの継承構造の中に組み込むことができます。例えば、リスト11のようなsealedクラスCocktailがあるとします(リスト3のインターフェースとは別物です)。
sealed class Cocktail { abstract fun show() }
このクラスを継承したdataクラスとしてGinBaseCocktailを定義するとしたら、リスト12のようなコードとなります(こちらも、リスト4のGinBaseCocktailとは別物です)。
data class GinBaseCocktail( val name: String, val ginVolume: Int ) : Cocktail() { override fun show() { println("${name}のジンの量は${ginVolume}") } }
このクラスはsealedクラスを継承したクラスなので、この段階では、CocktailクラスのサブクラスはGinBaseCocktailのみであることが保証されます。
さらにdataクラスでもあるので、例えばリスト13のコードを実行した場合、その実行結果は「GinBaseCocktail(name=ホワイトレディ, ginVolume=30)」となります。
val whiteLady = GinBaseCocktail("ホワイトレディ", 30) println(whiteLady)
さらに、リスト14のdataオブジェクトを作成し、そのオブジェクトのtoString()の値を表示したとしても、「NewYork」とオブジェクト名が表示されるようになります。
data object NewYork : Cocktail() { val name = "ニューヨーク" override fun show() { println("${name}です") } }
なお、先述のsealedの制約の通り、リスト11のCocktail、リスト12のGinBaseCocktail、および、リスト14のNewYorkは、同一パッケージ内で宣言されている必要があります。