前回に引き続きSwiftエンジニアがKotlinのチュートリアルを進めます。今回はDestructuring declarations and Data classesの章です。
http://try.kotlinlang.org/#/Examples/Destructuring%20declarations%20and%20Data%20classes/Destructuring%20declarations/Destructuring%20declarations.kt
自分が実装した分のリポジトリ
https://github.com/akatsuki174/KotlinTutorial
上記のDestructuringDeclarationsAndDataClasses.ktファイル
Destructuring declarations and Data classes
Destructuring declarations
fun main(args: Array<String>) {
val pair = Pair(1, "one")
val (num, name) = pair
println("num = $num, name = $name")
}
class Pair<K, V>(val first: K, val second: V) {
operator fun component1(): K {
return first
}
operator fun component2(): V {
return second
}
}
ここでは分解宣言というものを学びます。分解宣言をすると一度に複数の変数を作成できます。つまりこんなことができるということです。
val (num, name) = pair
複数の値を返すようにするために、componentN関数を用意しています(Nの部分は任意の整数です)。そのため、このように値を取得することもできます。
val pair = Pair(1, "one")
val num = pair.component1()
val name = pair.component2()
Data classes
data class User(val name: String, val id: Int)
fun getUser(): User {
return User("Alex", 1)
}
fun main(args: Array<String>) {
val user = getUser()
println("name = ${user.name}, id = ${user.id}")
// or
val (name, id) = getUser() // 分解宣言
println("name = $name, id = $id")
// or
println("name = ${getUser().component1()}, id = ${getUser().component2()}")
}
Kotlinにはデータを保持するだけのためのクラス、データクラスがありdata
を宣言時に付与します。データクラスを作ると、プライマリコンストラクタから推測してtoString()
, equals()
, hashCode()
, copy()
が使用できるようにしてくれます。これら関数の用例についてはAutogenerated functions
の章で扱います。
データクラスは以下の要件を満たす必要があります。
- プライマリコンストラクタは少なくとも1つのパラメータを有すること
- プライマリコンストラクタのパラメータは
val
またはvar
を付けること - abstract, open, sealed, innerにしない
※Kotlin1.1からデータクラスは他のクラスを拡張することができるようになりました。
最後にcomponentN
関数についてです。プライマリコンストラクタで宣言した数、順番に応じて作成されます。そのためgetUser().component1()
と書くとコンストラクタの1つめに書いたnameが返却されます。
Traversing a map
fun main(args: Array<String>) {
val map = hashMapOf<String, Int>()
map.put("one", 1)
map.put("two", 2)
for ((key, value) in map) {
println("key = $key, value = $value")
}
}
kotlinにもmapが存在し、上記のように使うことができます。以下のように要素を取得することもできます。
println("value = ${map.get("one")}")
println("value = ${map["two"]}")
初期化時に値を入れたい場合はこのように書くことができます。
val map2 = hashMapOf("一" to 1, "二" to 2)
println("value = ${map2.get("一")}")
また、JavaのようにLinkedHashMap, SortedMapも使えます。
val linkedMap = linkedMapOf("mikan" to "🍊", "apple" to "🍎")
val sortedMapOf = sortedMapOf("mikan" to "🍊", "apple" to "🍎")
HashMapに関する詳しい情報は公式のHashMapのページを御覧ください。
Autogenerated functions
data class User(val name: String, val id: Int)
fun main(args: Array<String>) {
val user = User("Alex", 1)
println(user) // toString()
val secondUser = User("Alex", 1)
val thirdUser = User("Max", 2)
println("user == secondUser: ${user == secondUser}")
println("user == thirdUser: ${user == thirdUser}")
// copy() function
println(user.copy())
println(user.copy("Max"))
println(user.copy(id = 2))
println(user.copy("Max", 2))
}
Data classes
の章でデータクラスについて軽く学びましたがここではもう少し深く学びます。データクラスはtoString()
, equals()
, hashCode()
, copy()
が自動で作られます。
toString()
:上記の例にあるようにprintln([オブジェクト])
で使うこともできますし、user.toString()
という形で呼び出すこともできます。出力結果はUser(name=Alex, id=1)
となります。
equals()
:中身が同じオブジェクトかどうかの判定は上記の例にあるようにuser == secondUser
で表すこともできますが、user.equals(secondUser)
という書き方をすることもできます。
hashCode()
:ハッシュコードを出力してくれます。63347075
のような形で出力されます。
copy()
:上記の例で出ているようにオブジェクトをコピーできます。上記の例の実行結果は以下の通りです。
println(user.copy()) // User(name=Alex, id=1)
println(user.copy("Max")) // User(name=Max, id=1) user.copy(name = "Max")と同義
println(user.copy(id = 2)) // User(name=Alex, id=2)
println(user.copy("Max", 2)) // User(name=Max, id=2)
感想
ファイル名の色の意味がわかりづらい
KotlinそのものではなくIDEAの話になります。
ある時、ファイル名が赤くなっているものがあることに気づきました。というか白と青まである。
赤ってことはエラーがあるのか??と思ったものの、正常に実行できます。調べてみたこと、試してみたことを考え合わせると、Gitへのコミット状態によって色が変化しているようでした。
ファイルの色 | 意味 |
---|---|
白 | 全てコミット済み |
青 | コミットされていない部分がある |
赤 | 新しく追加したファイルでまだコミットしていない |
Xcodeで言うと以下の状態と同じです。
マークの種類 | 意味 |
---|---|
なし | 全てコミット済み |
M | コミットされていない部分がある |
A | 新しく追加したファイルでまだコミットしていない |
個人的には色よりマークで示してくれた方が「え?エラー??」とか焦らずにいられて良いなと思いました。
分解宣言が便利そう
Swiftでも次のようにdictionaryを分解宣言することはできます。
let dictionary = ["one":1, "two":2, "three":3]
for (key, value) in dictionary {
print("key is \(key), value is \(value)")
}
しかしクラスに対して行うことはできないのでKotlinの方がより柔軟性が高く、使いどころの幅も広がるかなと思いました(ただ、プロパティが多くて分解時に変数が多く生成されてしまって逆に使い勝手(見栄え?)が悪くならないのだろうかと思ったりもしています)。