Kotlinのデータクラスとタプルを理解する【Androidアプリ開発】
Kotlinのデータクラスとタプルが面白かったので解説していく。一見、データクラスとタプルは何も関係ないように思えるかもしれないが、実はかなり近い仲間であると言って良い。普段なにげなく使っている関数も、どうやってできているのか中身の構造を知るとプログラミング言語自体が面白く見えてくる。プログラミング初心者から一歩先へ進みたいかたにおすすめな内容なので、ぜひ参考にしてみてほしい。
データクラス
構造体のデータ管理するためにclassを使いたい場合、次のようにプロパティだけもたせたクラスを作ることがあるだろう。
class Kami(val level:Int, val kanji: String, val kana:String)
このクラスは、修飾子dataをつけることで大変良いことが起こる。
data class Kami(
val level:Int = 20,
val kanji: String = "天照大神",
val kana:String = "アマテラスオオミカミ"
)
自動で次のメソッドを実装してくれるのだ。
- equals()
- hashCode()
- toString()
- copy()
- componentN()
分解宣言
たとえば、componentN()のメソッドが実装されたおかげで、次のように一度に変数をマッピングして取り出せる。
val defaultKami = Kami()
val (level, name, kana) = defaultKami
println("$level, $name, $kana")
これを 分解宣言(destructuring declaration) と呼ぶ。
もし、修飾子dataをつけなかったら、自前でcomponentNを実装しなければならない。
class Kami(
val level:Int = 20,
val kanji: String = "天照大神",
val kana:String = "アマテラスオオミカミ"
) {
operator fun component1(): Int = level
operator fun component2(): String = kanji
operator fun component3(): String = kana
}
Triple
ところでKotlinには、SwiftやPythonで使える可変可能なタプルが存在しない。しかし内容が限定されたPairやTripleといったクラスが用意されている。たとえばTripleはこんな感じ。
val (id, name, furigana) = Triple(5, "汐華初流乃", "しおばなはるの")
println("$id, $name, $furigana")
データクラスで行った分解宣言と似ていないだろうか?
それもそのはず、PairやTripleもデータクラスなのだ!
Tripleの定義をみると次のようになっている。
public data class Triple<out A, out B, out C>(
public val first: A,
public val second: B,
public val third: C
) : Serializable {
public override fun toString(): String = "($first, $second, $third)"
}
public fun <T> Triple<T, T, T>.toList(): List<T> = listOf(first, second, third)
Quadruple
Kotlin標準関数であるTripleの定義を真似て、Quadruple(クオドルプル)を作ってみよう。
internal typealias Serializable = java.io.Serializable
public data class Quadruple<out A, out B, out C, out D>(
public val first: A,
public val second: B,
public val third: C,
public val quad: D
) : Serializable {
public override fun toString(): String = "Quadruple data is ($first, $second, $third, $quad)"
}
public fun <T> Quadruple<T, T, T, T>.toList(): List<T> = listOf(first, second, third, quad)
Tripleの定義にプロパティquad を追加しただけで、とても簡単にQuadrupleができた。
それでは、実行してみよう。
fun main() {
val hero = Quadruple(5, "汐華初流乃", "しおばなはるの", "ゴールド・エクスペリエンス")
println(hero)
val list = hero.toList()
println(list)
}
実行してみると次のように出力された。
Quadruple data is (5, 汐華初流乃, しおばなはるの, ゴールド・エクスペリエンス)
[5, 汐華初流乃, しおばなはるの, ゴールド・エクスペリエンス]
1行目は、toString()をオーバーライドしているので期待通りの結果だ。
2行目は、listの中身が表示されている。今回、Quadrupleの値がInt型とString型の複数を持っているため、toListメソッドで返されるListはList<Any>型になっているので注意だ。
Pair
最後に、Pair型も使ってみよう。List<Pair>型のリストをfor文で回すと、次のように値を取りだせる。すでに分解宣言を知っているので、まったく不思議ではない。
val fruits = listOf<Pair<String, Int>>(
Pair("バナナ", 100),
Pair("りんご", 198)
)
for ((name, cost) in fruits) {
println("果物: $name, 値段: $cost")
}
ところで、Pair型のオブジェクトを作るときは次のように書くことができる。
val pair:Pair<String, Int> = "A" to 1
これはMap型のオブジェクトを作るときにやっていることだ。だから当然、Map型もPair型と同じようにfor文でkeyとvalueをマッピングできるわけだ。
val capitals = mapOf<String, String>(
"日本" to "東京",
"アメリカ合衆国" to "ワシントンD.C."
)
for ((k, v) in capitals) {
println("国: $k, 首都: $v")
}
参考
Basic Syntax - Kotlin Programming Language Idioms - Kotlin Programming Language
▼ こんな記事も書いてます。