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

前回に引き続きSwiftエンジニアがKotlinのチュートリアルを進めます。今回はBasic syntax walk-throughの章です。
http://try.kotlinlang.org/#/Examples/Basic%20syntax%20walk-through/Use%20a%20conditional%20expression/Use%20a%20conditional%20expression.kt

# 自分が実装した分のリポジトリ

https://github.com/akatsuki174/KotlinTutorial
上記のBasicSyntaxWalkThrough.ktファイル

# Basic syntax walk-through

## Use a conditional expression

ここでは`if`の使い方と関数の使い方が学べます。

```kotlin
fun max(a: Int, b: Int) = if (a > b) a else b
```

ifに関しては前回の記事にも書いたので省略します。関数についてですが、以下のことについては今までのチュートリアルでも学んだことです。

* 関数宣言は`fun`であること
* selfなどなしに呼び出せること

今回はそれに加え単一式関数の記法が出てきました。単一の値を返すだけであれば`=`で繋いで1行で書くことができます。また、型推論ができる場合には戻り値の型を指定する必要はありません。ということで、以下のような書き方も可能です。

```kotlin
// 戻り値の型を指定
fun max(a: Int, b: Int):Int = if (a > b) a else b

// ブロックを使用
fun max(a: Int, b: Int): Int {
    return if (a > b) a else b
}
```

引数については`名前:タイプ`の順で書かれること、引数を複数持つ場合はカンマで区切るということがわかります。また、Javaとは異なり名前付き引数を使うこともできます。今回の例の場合だと、次のように書くことができます。

```kotlin
max(a = args[0].toInt(), b = args[1].toInt())
```

ifに関しての公式資料は[こちら](http://kotlinlang.org/docs/reference/control-flow.html#if-expression)
単一式関数に関しての公式資料は[こちら](http://kotlinlang.org/docs/reference/functions.html#single-expression-functions)

## Null-checks

```kotlin
fun main(args: Array<String>) {
    if (args.size < 2) {
        println("No number supplied");
    } else {
        val x = parseInt(args[0])
        val y = parseInt(args[1])

        // x,yにnullが入っている可能性がある
        if (x != null && y != null) {
            print(x * y)
        } else {
            println("One of the arguments is null")
        }
    }
}

// もしstrが数字ではなかったらnullを返す
fun parseInt(str: String): Int? {
    try {
        return str.toInt()
    } catch (e: NumberFormatException) {
        println("One of the arguments isn't Int")
    }
    return null
}
```

JavaではnullオブジェクトにアクセスするとNullPointerExceptionが発生していました。KotlinはコードからNullPointerExceptionが発生しないように作られています。NullPointerExceptionが発生するのは以下の状態の時です。

* 明示的に`throw NullPointerException()`をした時
* 一定条件下で`!!`を使った時
* 外部のJavaコードがNullPointerExceptionになった時
* 初期化の際のデータ不備（初期化されていないのにどこかで使われている）

Swiftのように、ある変数がnullになりうる場合は「?」を付けてそれを明示しなければなりません。
「安全でない」場合は以下のような表示が出ます。
![スクリーンショット 2017-04-16 10.03.05.png](https://qiita-image-store.s3.amazonaws.com/0/100736/cf095ee7-fcd8-5614-c4c6-2a86161a2415.png)

null安全に関する公式資料は[こちら](http://kotlinlang.org/docs/reference/null-safety.html#null-safety)

## is-checks and smart casts

```kotlin
fun main(args: Array<String>) {
    println(getStringLength("aaa"))
    println(getStringLength(1))
}

fun getStringLength(obj: Any): Int? {
    if (obj is String)
        return obj.length // no cast to String is needed
    return null
}
```

`is`は型チェックを行うことができ、否定型は`!is`になります。

`is`で型チェックを行うと自動的にキャストが行われるため、上の例でも`obj`は型チェック後すぐにString型として使えています。公式に例として載っている以下のコードはとてもクールだと思います。

```kotlin
if (x !is String) return
print(x.length) // x is automatically cast to String
```

また以下のようなコードも書けます。型チェック後すぐにString型として使うことができています。

```kotlin
if (x !is String || x.length == 0) return
```

`when`との組み合わせでも使われます。

```kotlin
when (x) {
  is Int -> print(x + 1)
}
```

クラスと継承に関する公式資料は[こちら](http://kotlinlang.org/docs/reference/classes.html#classes-and-inheritance)
スマートキャストに関する公式資料は[こちら](http://kotlinlang.org/docs/reference/typecasts.html#smart-casts)

## Use a while-loop

```kotlin
fun main(args: Array<String>) {
    var i = 0
    while (i < args.size)
        println(args[i++])
}
```

`while`と`do..while`の書き方を学ぶサンプルになります。`do.while`で書く場合はこのような形を取ります。

```kotlin
do {
    println(args[i++])
} while (i < args.size)
```

whileループに関する公式資料は[こちら](http://kotlinlang.org/docs/reference/control-flow.html#while-loops)

## Use a for-loop

```kotlin
fun main(args: Array<String>) {
    for (arg in args)
        println(arg)
    println()
    for (i in args.indices)
        println(args[i])
}
```

今度はforループの使い方についてです。前回の記事でも書いた通り、このような書き方もできます。

```kotlin
for ((index, value) in args.withIndex())
    println("$index: value is $value")
```

forループに関する公式資料は[こちら](http://kotlinlang.org/docs/reference/control-flow.html#for-loops)

## Use ranges and in

```kotlin
fun main(args: Array<String>) {
    val x = args[0].toInt()
    // xがある範囲内に入っているか確認
    val y = 10
    if (x in 1..y - 1)
        println("OK")

    //範囲内で繰り返し
    for (a in 1..5)
        print("${a} ")

    println()
    val array = arrayListOf<String>()
    array.add("aaa")
    array.add("bbb")
    array.add("ccc")

    // ある数値が範囲外か確認
    if (x !in 0..array.size - 1)
        println("Out: array has only ${array.size} elements. x = ${x}")

    // コレクションがあるオブジェクトを含むか確認
    if ("aaa" in array) // collection.contains(obj) is called
        println("Yes: array contains aaa")
    if ("ddd" in array) // collection.contains(obj) is called
        println("Yes: array contains ddd")
    else
        println("No: array doesn't contains ddd")
}
```

`range`, `in`とともに使われることがある関数として`downTo`、`step`があり、以下のような使い方をします。

```kotlin
// 数を逆順に処理したければdownTo関数が使える
for (i in 5 downTo 1) print(i)
// xつごとに処理を実行したい場合はstep関数が使える
for (i in 1..4 step 2) print(i) // 2つごとに処理を実行
```

Rangeに関する公式資料は[こちら](http://kotlinlang.org/docs/reference/ranges.html#ranges)

## Use when

```kotlin
fun main(args: Array<String>) {
    cases("Hello")
    cases(1)
    cases(0L)
    cases(MyClass())
    cases("hello")
}

fun cases(obj: Any) {
    when (obj) {
        1 -> println("One")
        "Hello" -> println("Greeting")
        is Long -> println("Long")
        !is String -> println("Not a string")
        else -> println("Unknown")
    }
}

class MyClass() {
}
```

whenに関しては前回の記事でも書いたのでさらっと書きます。`switch`文のような形で使うことができます。条件を満たすものが現れるまで評価し続けます。`is`を使って型判定も行うことができます。

# 感想

## 名前付き引数があるので読みやすい

Javaとは異なり引数に名前を付けられるのは読みやすくていいなと思います。ただ引数の名前は任意であるため、IntelliJ IDEAで関数名を自動補完で入力した時に引数名も入力されないのは少し残念だなと思いました。

↓自動補完時の挙動
![スクリーンショット 2017-04-22 22.34.07.png](https://qiita-image-store.s3.amazonaws.com/0/100736/14ccdfce-6678-3768-3e54-83642054d5dc.png)

![スクリーンショット 2017-04-22 22.34.17.png](https://qiita-image-store.s3.amazonaws.com/0/100736/7211531e-54a8-5933-1720-abb08cec40a9.png)

## 型周りが扱いやすい

↓これって結構すごいことだと思っています。

```kotlin
fun cases(obj: Any) {
    when (obj) {
        1 -> println("One")
        "Hello" -> println("Greeting")
        is Long -> println("Long")
        else -> println("Unknown")
    }
}
```

Swiftで同じように書こうとしてもできないですし（たぶん）。

![スクリーンショット 2017-04-22 22.54.01.png](https://qiita-image-store.s3.amazonaws.com/0/100736/2efcbbe3-e77c-e89d-a408-253285044717.png)