data class入門

今回はdata classの話をします。 クラスの話はもうちょっと後にするつもりなのだけど、とりあえずdata classは使いたいのでdata classの簡単な使い方を先に。

Data classes - Kotlin Documentation

data class入門

data classは、複数のデータをまとめて持つのに使う機能です。 割と単純なので特に難しい事も無いのですが、実際に使う所を見ないと「なんでそんな機能あるの?」って思って終わりだと思うので、 ここまで登場しませんでした。

でも今回、ListViewの編集とDateの説明が終わったので、ようやく具体的な使い道をもとに説明出来ます。

という事で、まずは使い道から見ていきましょう。

前回の日付の所でやった「ListViewに挑む!編集編」に、投稿時間をつける所のリストがいまいち

data classを使いたくなるシチュエーションとして、日付を扱う、Date入門の最後でやった、課題: 「ListViewに挑む!編集編」に、投稿時間をつける のリストの所を考えます。

上記のリンク先では、以下のようにしました。

val listData = mutableListOf<String>()
val listDt = mutableListOf<Date>()

でもこの二つは毎回セットでaddされるし、セットでlistData[position]listDt[position]されます。 消す時も両方同じ所を消さないといけません。

こういうのはいかにも片方操作し忘れとかがあってバグりやすい。

そこでStringとDateをまとめて一つの要素としてListに追加していくのが良い解決策です。 それを実現するのがdata classです。

以下data classの具体例を見ていきましょう。

data classの簡単な例

まずdata classの例を見ていきたいと思います。

data class は以下のように使います。

data class TwoInt(val v1: Int, val v2: Int) fun main() { val ti1 = TwoInt(1, 2) println(ti1.v1) println(ti1.v2) }

以下、細かく見ていきましょう。

data classの文法

まずdata classは最初にdata class クラス名で始まります。 クラス名は大文字始まりで単語の区切り大文字という慣習になっています。 つまりTwoIntとかUserDataとかそういうものになります。

そしてクラス名の後にカッコが続き、変数定義のようなものが、型付きで並びます。

代入する場合はvar、代入しない場合はvalですが、あまりvarを使う事は無いのでvalとおぼえてしまっても良いでしょう。

こうしてdata classが定義出来ます。data classは、新しい型となります。この場合はTwoInt型が出来る事になります。

次に使い方です。data classは定義すると、そのクラス名を関数のように使う事が出来て、 そのreturnはそのdata classのデータとなります。先ほどの例ではTwoIntというのを関数のように使う事が出来て、 TwoInt(1, 2)とすると、TwoInt型のデータが作れる訳です。

各構成要素の触り方はドットと変数名

さて、val ti1 = TwoInt(1, 2) とti1変数にデータを入れられました。 この1とか2にはどうアクセスするか?というと、ti1.v1とかti1.v2というように、 ドットとdata classを定義した時の変数名でアクセスします。

これで基本的な使い方は終わりです。 ちょっと幾つか例とか練習問題とかを見ていきましょう。

data classを使った例いろいろ

幾つか例を見ていきましょう。さきほどはどっちもIntだったので、次はStringとIntの例を。 ユーザー名とスコアを持たせる、というのは割と良くあります。

data class User(val name: String, val score: Int) fun main() { val user1 = User("ほげいか", 95) val user2 = User("karino2", 72) println("${user1.name}のスコアは${user1.score}点です") println("${user2.name}のスコアは${user2.score}点です") }

また、これをListに入れる事もできる。

data class User(val name: String, val score: Int) fun main() { val mlist = mutableListOf<User>() mlist.add(User("ほげいか", 95)) mlist.add(User("karino2", 72)) for(user in mlist) { println("${user.name}のスコアは${user.score}点です") } }

次に「ListViewに挑む!編集編」で、入力されたテキストが追加された時間をDateで保存するような場合を考える。 Dateなのでimport文を追加して、以下のような感じになる。

import java.util.Date data class Post(val item: String, val date: Date) fun main() { val mlist = mutableListOf<Post>() mlist.add(Post("これは一番目の項目です", Date())) mlist.add(Post("これは二番目の項目です", Date())) for(post in mlist) { println("「${post.item}」は${post.date}に追加されました") } }

では自分でも幾つか作ってみましょう。

課題: ユーザー名と投稿数を持つ、SNSUserを作れ

ユーザー名はname, 投稿数はpostNumとしましょう。

// TODO: ここにSNSUserを作る fun main() { val user1 = SNSUser("karino2", 920) val user2 = SNSUser("ほげいか", 312) println(user1.name) println(user1.postNum) println(user2.name) println(user2.postNum) }

課題: 名前と攻撃力と防御力を持つCharacterを作れ

名前はname, 攻撃力はattack、防御力はdefenseとしますか。

// TODO: ここにCharacterを作る fun main() { val chara1 = Character("メタルスライム", 10, 999) val chara2 = Character("士翼号", 999, 20) for(chara in listOf(chara1, chara2)) { println("${chara.name} の攻撃力は${chara.attack}、防御力は${chara.defense}です") } }

課題: アイテムをたくさん持つアイテムボックスのdata classを作れ

アイテムボックスは名前がnameであって、Stringのアイテムをたくさん持つとします。とりあすList<String>でitemsという名前でいいでしょう。

// TODO: ここにItemBoxを作る fun main() { val box1 = ItemBox("ドラクエのアイテムボックス", listOf("薬草", "毒消し草", "布の服", "ひのきの棒")) val box2 = ItemBox("FFのアイテムボックス", listOf("ポーション", "エーテル", "ミスリルソード")) for(box in listOf(box1, box2)) { println("${box.name}:") for(item in box.items) { println(" ${item}") } println("") } }

課題: 以下をtrueになるように変更せよ

要素にアクセスする方もやってみます。

data class User(val name: String, val postNum: Int) fun main() { val user1 = User("karino2", 920) val user2 = User("ほげいか", 312) // TODO: 以下の==の左側を変更してtrueになるようにせよ println(user1 == "karino2") println(user1 == 920) println(user2 == "ほげいか") println(user2 == 312) }

課題: 前回の日付の入門でやった、「ListViewに挑む!編集編」に、投稿時間をつけたものをdata class化しよう

という事でdata classについての簡単な説明をしたので、ついに日付を扱う、Date入門の最後でやった、課題: 「ListViewに挑む!編集編」に、投稿時間をつける でリストが二つだった問題を、 data classを使ってもっといい感じにしましょう。

  1. Postというdata classを作り、項目名とDateをもたせる
  2. listDataをPostのMutableListにする
  3. getViewで時刻表示用TextViewに時刻をセットするあたりを修正
  4. アイテムを追加するボタンのsetOnClickListenerで、Postを追加するようにして、その時点でのDate()を持たせる

以下各ステップを見ていきましょう。

1. Postというdata classを作り、項目名とDateをもたせる

Postというdata classで、StringのitemとDate型のdateを持たせるとしましょう。 このPostというdata classは、コードの置き場所入門の「1番目の区画」に置きます。

ついに1番目の区画にコードを書く時が来ましたね。

ここでDateの所が赤くなったら、上にカーソルを移動して、Alt+Enterでimportを追加しましょう。

2. listDataをPostのMutableListにする

これは特に解説の必要は無いですかね。

3. getViewで時刻表示用TextViewに時刻をセット

itemLabelにセットしているのと同様に、itemDateにも日付をセットします。toStringでいいでしょう。

日付は、以下のような感じでセットします。

val item = listData[position]

view.findViewById<TextView>(R.id.itemDate).text = item.date.toString()

itemLabelの方もこんな感じで変更します。

4. アイテムを追加するボタンのsetOnClickListenerで、Postを追加するようにして、その時点でのDate()を持たせる

setOnClickListenerの中で、以下みたいな感じに書きます。

val text = findViewById(R.id.edit1).text.toString()
val post = Post(text, Date())
listData.add(post)

変数使わなくてもいいですが、このくらいは使った方が読みやすいとは思う。

以上で、二つのリストを同時に追加したり削除したり、という事が不要になりました。やったね!

まとめ: リストに二つ以上何か持たせたい時はdata classが便利

リストに、投稿された文字列と時間の二つを追加したい、みたいな時にはdata classが便利なので使っていきましょう。

おまけ:クラスの用語いろいろ

このシリーズはAndroid側とkotlin言語側の話が交互に続いていく感じになっていますが、 Android側の最難関はおそらくListViewです。

一方kotlin側の最難関はクラスとなります。

クラスは何が難しいって用語がめちゃくちゃ多い。 そこで割と単純なdata classの段階で用語を幾つか先取りしておきます。 現時点では次回予告の前にちょっと思わせぶりなクールなキャラが出てきて意味深な事言って次回に続くって感じだな、と思っておけばいいです。

クラスとオブジェクト(またはインスタンス)

data class TwoInt(val v1: Int, val v2: Int)でTwoIntクラスという名前の型が出来ます。 そして、TwoIntクラスの型のデータをオブジェクトと呼びます。

少しややこしいですね。型とは何か?で少し触れたように、 型というのは、データのカテゴリでした。

ストII、ストIIダッシュ、スーパーストII、スパIIXなどがデータで、ストIIシリーズが型、というものでした。

つまり一つの型というカテゴリに対し、そのカテゴリにあてはまるデータが複数ある訳です。

そしてこのカテゴリにはIntとかStringとかList<String>とかがある訳だけれど、 今回新しくTwoIntというカテゴリを作れるようになった訳です。

ストIIシリーズという型にはいろんなストIIのタイトルがあるように、 TwoIntというカテゴリにも、(1, 2)というデータを持つTwoIntや、(8, 3)を持つTwoIntなど、いろんなデータが存在します。

このデータを、クラスの時は「オブジェクト」とか「インスタンス」と呼ぶ事になっているという訳です。 「オブジェクト」と「インスタンス」はほぼ同じ意味です。起き昇とリバーサル昇竜くらいの違いです。

最初のうちはややこしいかもしれないけれど、クラスを自分で作っていくようになるとこのインスタンスとクラスの区別みたいなのは何度も出てくるようになるので、 使っていくと慣れます。

という事で我らもこれから使っていく事にする。

以上をまとめると、以下のようになります。

  • data class TwoInt(val v1: Int, val v2: Int)でTwoIntクラスが出来る
  • TwoInt(1, 2) などと呼ぶと、TwoIntクラスのインスタンスが作れる
  • TwoInt(7, 3) などと呼んでも、TwoIntのクラスのインスタンスは作れる
  • 一つのTwoInt型からインスタンスはたくさん作れる
  • TwoInt型とTwoIntクラスはほとんど同じ意味

とりあえず用語はややこしいので何度も使って慣れる方がいい。という事で以後バリバリ使っていきましょう。

メンバ変数v1, v2

先ほどの定義を再掲します。

data class TwoInt(val v1: Int, val v2: Int)

このv1とv2をメンバ変数と呼びます。 メンバ変数というのはこれまでもActivityの所に定義している変数をそう呼んでいました。 あれも内部的には全く同じものです。 クラスを真面目にやると両者が同じものなのが分かりますが、とりあえずここではdata classのこれらの変数もメンバ変数と呼ぶ、 とだけ覚えておいてください。