Kotlinのチュートリアルをやってみた④

前回に引き続き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

```kotlin
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
    }
}
```

ここでは分解宣言というものを学びます。分解宣言をすると一度に複数の変数を作成できます。つまりこんなことができるということです。

```kotlin
val (num, name) = pair
```

複数の値を返すようにするために、componentN関数を用意しています（Nの部分は任意の整数です）。そのため、このように値を取得することもできます。

```kotlin
val pair = Pair(1, "one")
val num = pair.component1()
val name = pair.component2()
```

## Data classes

```kotlin
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

```kotlin
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が存在し、上記のように使うことができます。以下のように要素を取得することもできます。

```kotlin
println("value = ${map.get("one")}")
println("value = ${map["two"]}")
```

初期化時に値を入れたい場合はこのように書くことができます。

```kotlin
val map2 = hashMapOf("一" to 1, "二" to 2)
println("value = ${map2.get("一")}")
```

また、JavaのようにLinkedHashMap, SortedMapも使えます。

```kotlin
val linkedMap = linkedMapOf("mikan" to "🍊", "apple" to "🍎")
val sortedMapOf = sortedMapOf("mikan" to "🍊", "apple" to "🍎")
```

HashMapに関する詳しい情報は[公式のHashMapのページ](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-hash-map/)を御覧ください。


## Autogenerated functions

```kotlin
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()`：上記の例で出ているようにオブジェクトをコピーできます。上記の例の実行結果は以下の通りです。

```kotlin
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の話になります。

ある時、ファイル名が赤くなっているものがあることに気づきました。というか白と青まである。
![スクリーンショット 2017-04-23 1.42.29.png](https://qiita-image-store.s3.amazonaws.com/0/100736/784f4f02-086c-fc1d-59c7-ae5d514fa273.png)

赤ってことはエラーがあるのか？？と思ったものの、正常に実行できます。調べてみたこと、試してみたことを考え合わせると、Gitへのコミット状態によって色が変化しているようでした。

| ファイルの色 | 意味 |
|:--|:--|
| 白  | 全てコミット済み  |
| 青  | コミットされていない部分がある  |
| 赤  | 新しく追加したファイルでまだコミットしていない  |

Xcodeで言うと以下の状態と同じです。

![スクリーンショット 2017-04-23 9.30.14.png](https://qiita-image-store.s3.amazonaws.com/0/100736/d545ecd0-84d1-d07d-3632-6a697dc8aa3a.png)

| マークの種類 | 意味 |
|:--|:--|
| なし  | 全てコミット済み  |
| M  | コミットされていない部分がある  |
| A  | 新しく追加したファイルでまだコミットしていない  |

個人的には色よりマークで示してくれた方が「え？エラー？？」とか焦らずにいられて良いなと思いました。

## 分解宣言が便利そう

Swiftでも次のようにdictionaryを分解宣言することはできます。

```swift
let dictionary = ["one":1, "two":2, "three":3]        
for (key, value) in dictionary {
    print("key is \(key), value is \(value)")
}
```

しかしクラスに対して行うことはできないのでKotlinの方がより柔軟性が高く、使いどころの幅も広がるかなと思いました（ただ、プロパティが多くて分解時に変数が多く生成されてしまって逆に使い勝手（見栄え？）が悪くならないのだろうかと思ったりもしています）。