Kotlinの基本的な言語仕様を学習したまとめ

## はじめに

久しぶりにネイティブなAndroidアプリを作ってみようと思い、2019年時点の開発環境や開発言語について調べてみました。
Webや書籍をみると、ほとんどが開発言語としてKotlinを利用しているため、まずはKotlinの開発環境の構築と学習を進めていきます。

## Kotlin とは

kotlinは、Java言語をもっと簡潔かつ安全になるように改良した産業利用向け汎用言語として開発されたもの。
型推論のある静的型付けのオブジェクト指向プログラミング言語であり、Scalaの関数型プログラミング言語や、C#のジェネリクスの要素などを取り入れている。
Java言語と同様に、Javaバイトコードにコンパイルされて、Java仮想マシン上で動作する。
Java言語との連携が言語仕様として組み込まれているため、Java言語のプロジェクトにKotlinのソースを段階的に追加したり、KotlinからJava言語のクラスやメソッドを呼び出することもできる。

## Jetbrains とは

Androidの開発言語としてKotilnが正式に採用されたため、Kotlinという開発言語とセットでGoogle社の名前が目立つが、Kotlinを開発しているのはJetbrains社である。
Jetbrains社は、IntelliJ IDEAというJavaの統合開発環境（IDE）の開発や販売を行っている企業である。
過去にAndroidアプリを開発していたころは、EclipseとADT Pluginの組み合わせが主流でしたが、現在はAndroid Studioが主流となってる。
このAndroid Studioも、Jetbrains社がIntelliJ IDEAをAndroid開発用にカスタマイズしたもの。
ちなみに、Jetbrains社はPythonの統合開発環境であるPyCharmを開発していることでも有名ですね。

## 開発環境の準備

Kotlinの統合開発環境として、Jetbrains社のIntelliJ IDEAを利用してみました。

### IntelliJ IDEA Community Edition のインストール

1. JetBrains社のWebサイトから、IntelliJ IDEA Community Edition をダウンロードします。

    [Download IntelliJ IDEA](https://jetbrains.com/idea/download)

1. ダウンロードしたセットアップファイルを実行します。

1. [Next]ボタンをクリックします。

    ![IntelliJ_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/ef0457cf-55f9-5e9d-c05b-e525c5baeaff.png)

1. インストール先のフォルダを指定して[Next]ボタンをクリックします。

    ![IntelliJ_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/7430d042-299c-de58-5301-29f25fe80dcb.png)

1. 必要に応じて設定を変更し、[Next]ボタンをクリックします。Kotlinのソースファイル（.kt）を関連付ける場合は、[.kt]をチェックします。

    ![IntelliJ_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/8d515ef1-894d-6411-0837-559e6ce6d1a5.png)

1. [Install]ボタンをクリックします。

    ![IntelliJ_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/fef7c913-4b35-e8e8-72e0-aa729a654f9a.png)

1. インストール完了後、[Finish]ボタンをクリックしてセットアップを終了します。

    ![IntelliJ_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/41f2da10-a993-2960-c96d-f2334032ce64.png)

### IntelliJ IDEA Community Edition の起動

1. インストールしたIntelliJ IDEA Community Edition を起動します。

    ![IntelliJ_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/3c9bb127-619a-bfca-bb4a-261dc974ba24.png)

1. 初回インストールで設定ファイルを指定する必要が無ければ、インポートしないを選択して[OK]ボタンをクリックします。

    ![IntelliJ_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/2d90508d-bd19-f04f-3215-2a052e426aed.png)

1. 好きな配色を選んで[Next]ボタンをクリックします。

    ![IntelliJ_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/74e8773b-79e1-2efd-edd1-d534565f13e5.png)

1. 必要な物があれば有効化して[Next]ボタンをクリックします。

    ![IntelliJ_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/5f58b8ed-b53c-63a7-daf9-b92b940713a4.png)

1. 必要な物があればインストールして[Next]ボタンをクリックします。

    ![IntelliJ_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/a2ef9bfd-95fc-e609-538c-74314db9ffa1.png)

1. IntelliJ IDEA Community Edition が起動しました。

    ![IntelliJ_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/01a8d1db-866a-7027-7448-2b2db4cd3579.png)

### IntelliJ IDEA Community Edition でプロジェクトの作成

1. Welcome画面の[Create New Project]をクリックします。

    > Welcome画面に戻る場合は、[File]→[Close Project]でプロジェクトを閉じます。

1. Kotlin の JVM | IDEA を選択して[Next]ボタンをクリックします。

    ![IntelliJ_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/4464e23b-3176-39af-f909-cb2c7cf490a3.png)

1. プロジェクト名や場所およびSDKを指定して[Finish]ボタンをクリックします。

    ![IntelliJ_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/5dec8f20-442f-fd88-6357-c8b10f8f2506.png)

1. プロジェクトが作成されました。

    ![IntelliJ_14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/f24b2ea9-aff4-c732-d43d-662fd62ffcdb.png)

1. 左ペインのプロジェクトツールウィンドウのツリーを展開すると、このプロジェクトに関連するファイルなどを確認できます。

    ![IntelliJ_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/c2dbf930-11b9-8a06-a749-f4dbf4146419.png)

1. ソースファイルを作成する場合は、ツリーのsrcを右クリックして[New]→[Kotlin File/Class]を選択します。

    ![IntelliJ_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/7b518cdf-84c7-b9fb-a0e1-6bf2aedb30a9.png)

1. 作成するファイルの種類を選択し、ファイル名を入力します。

    ![IntelliJ_17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/d2a56ebc-8d26-6c2c-cfc5-e5c98363b5da.png)

1. 作成したファイルにコードを入力します。

    ```kotlin
    fun main(args: Array<String>) {
        println("Hello, Kotlin!")
    }
    ```

    ![IntelliJ_18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/e6951c6d-1d0a-60f1-5702-6ae9a2d27125.png)

1. ▶（ランボタン）をクリックして[Run 'ファイル名']を選択することで実行できます。

    ![IntelliJ_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/fce3c7bb-2d8c-fbf5-82b2-28b1529f18e8.png)

1. 実行結果は画面下部のコンソールに出力されます。

    ![IntelliJ_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/bf4c9a42-bddf-15c4-24fc-76e3e5a7469b.png)

1. Kotlinをちょっと試してみたいときは、REPL（read, evaluate, print, loop）を起動します。

    ![IntelliJ_21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/f1e6ff9c-3481-3236-46c7-eed627b6b691.png)

1. コードを入力して、[Ctrl+Enter]で実行できますので便利です。

    ![IntelliJ_22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/5ae8a0ad-1629-6609-ee32-690d2602c468.png)

## Kotlinの学習とメモ

Kotlinを学習して覚えたことや気になったことなど書き留めます。
開発言語の経験値は C#(7.x) > Java(7あたり) > Python(3.x) > JavaScript(ES6) な感じを前提としたメモです。
後半は息切れ気味ですが、基本的な言語仕様はおおよそ網羅できたかなと思います。
理解が甘い所が多々ありますので順次更新していきます。

## 参考にさせて頂いた情報

* [Kotlin - Android Developers](https://developer.android.com/kotlin?hl=ja)
* [３０分で覚えるKotlin文法](https://qiita.com/k5n/items/cc0377b75d8537ef8a85)
* [最初に押さえておきたいKotlin言語仕様](https://qiita.com/ssuzaki/items/0ab14379e2d30e807e9a)
* [Kotlinチートシート](https://qiita.com/emboss369/items/c39f196e703447820b47)
* [Android開発を受注したからKotlinをガッツリ使ってみたら最高だった](https://qiita.com/omochimetaru/items/98e015b0b694dd97f323)

### 変数

* 変数の場合は **var** 変数名 : 型
* Kotlin には参照型しかない（プリミティブ型は用意されていない）
* 行末のセミコロンはいらない（あってもよい）

```kotlin
var a : Long = 1000L
var b : Int = 100
var c : Short = 10
var d : Byte = 1
var e : Float = 1.1F
var f : Double = 3.14
var g : String = "Hello!"
var h : Char = 'X'
var i : Boolean = true
```

* もちろん型推論できる（strはString型となる）

```kotlin
var str = "Good!"
```

* 読取専用変数の場合は **val** 変数名 : 型

```kotlin
val num : Int = 20
```

* 宣言と初期化をわけることもできる

```kotlin
val num : Int
num = 20
```

### 定数

* アンダースコア区切りの大文字が一般的

```kotlin
const val MAX_COUNT: Int = 1024
```

### 演算

* インクリメントとデクリメントが助かる

```kotlin
a + b   // 加算
a - b   // 減算
a * b   // 乗算
a / b   // 除算
a % b   // 剰余
a++     // 前置インクリメント
++b     // 後置インクリメント
a--     // 前置デクリメント
--b     // 後置デクリメント
```

### 比較・論理演算

* ===と!==はJavaScriptの厳密等価演算子ではなく参照の比較となる

```kotlin
a < b   // 小なり
a <= b  // 以下
a > b   // 大なり
a >= b  // 以上
a == b  // 構造等価
a != b  // 非等価
a === b // 同じ参照
a !== b // 違う参照
!a      // 論理否定
a && b  // 論理積
a || c  // 論理和
```

### 変換

* Intから文字列への変換

```kotlin
val str = num.toString()
```

* StringからIntへの変換

```kotlin
val num: Int = "3.14".toIntOrNull() ?: 0
```

* DoubleからIntへの変換

```kotlin
val ret1 = doubleValue.toInt()
var ret2 = doubleValue.roundToInt()
```

### 文字列

* "${}"で文字列テンプレート

```kotlin
val str = "Hoge"
val num = 100
val text = """
Moge
"""
println("文字列＝$str、数値＝${num * 2}") // 式も埋め込める
```

* このあたりはC#やPythonなどと同様（下記は一部のみ）

```kotlin
val text = "Hello,Goodby"
text.length
text.toUpperCase()
text.toLowerCase()
text.startsWith("He")
text.substring(0,3)
text.replace("H","h")
text.split(",").joinToString("-")
```

### 日付

* 文字列から日付への変換

```kotlin
// import java.time.LocalDate
// import java.time.format.DateTimeFormatter
val date = LocalDate.parse("2019-12-04")
val formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd")
val formattedDate = date.format(formatter)
plintln(formattedDate)
```

### 分岐（if式）

* if文ではなくif式だから判定結果を代入できる

```kotlin
val num = 100
val str = "Hello"
val ret = if( num in 50..100) { // Range指定
    println("A")
    "A"
} else if (str === "Hello" || num != 100) { // 論理演算子
    println("B")
    "B"
} else {
    println("C")
    "C"
}
println("Result $ret" )
```

### 分岐（when式）

* switch式みたいなもの
* inで範囲指定もできる

```kotlin
val num = 10
val ret = when (num){ // When式
    10 -> "A"
    in 11..20 -> "B" // Range指定
    else -> "C"
}
println("Result $ret" )
```

### 関数

* デフォルト引数を設定できる

```kotlin
private fun createMessage(num: Int = 50, str: String): String {
    val ret = when (num) {
        in 0..49 -> "A $str"
        in 50..99 -> "B $str"
        else -> "C $str"
    }
    return ret
}
```

### 単一式関数

* 単一式の場合はこのように簡略できる

```kotlin
private fun createMessage(num: Int = 50, str: String) =
    when (num) {
        in 0..49 -> "A $str"
        in 50..99 -> "B $str"
        else -> "C $str"
    }
```

### Unit関数

* いわゆるvoid関数だけどジェネリクスに対応するためにUnit型となる

```kotlin
private fun showMessage(msg: String) = plintln("$msg")
```

### Nothing型の関数

* 例外をスローするため制御が戻らない

```kotlin
inline fun TODO(): Nothing = throw NotImplementedError()
```

### 無名関数

* 関数型を利用するほうが多いかもしれない

```kotlin
{
    val num = 10
    plintln("$num")
}()
```

### 関数型

* C#のラムダ式に近いからわかりやすい
* 引数が1つの場合は暗黙の引数itを利用できることが特徴

```kotlin
val piyo: (String) -> String = {
    "$it" + "001"
}

val payo: (Int, String) -> String = { num, str ->
    "$num" + str
}
```

* 関数型を返す関数もできる

```kotlin
private fun createFunc(): (String) -> String {
    return { str: String ->
        str + "001"
    }
}
```

### null 許容型

* NULLを許容する場合はnull許容型を明示的に指定する

```kotlin
var c: String = "Hello!"
c = null // NG!
var c: String? = "Nomad"
c = null // OK!
```

### セーフコール演算子

* NULLの場合は評価しない

```kotlin
var str: String?
str!.length

val ret = str?.let { // ? セーフコール演算子
    if (it.isNotBlank()) {
        it
    } else {
        "OK"
    }
}
```

### null合体演算子

* NULLの場合は右辺を返す（C#では"??"）

```kotlin
var str: String? = null
val ret: String = str ?: "OK"　// ?: null合体演算子
```

### apply

* 一連の関数を呼び出す。Visual BasicのWithみたいなものかな

```kotlin
val file = File("hoge.txt").apply {
    setReadable(true)
    setWritable(true)
    setExecutable(false)
}
```

### let

* 渡された変数をラムダのスコープとしてitで参照できるようにする

```kotlin
val num: Int = 10
val squared = num.let {
    it * it // num → it
}
```

* null合体演算子と組み合わせると便利

```kotlin
val num: Int? = 10
var str: String = num?.let { "$it is not null" } ?: "null"
```

### run

* 一連の関数を呼び出す。レシーバーではなく結果を返す

```kotlin
fun createMessage(str: String) = str + "message"
var str = "Hello"
str.run(::createMessage).run(::println) // 関数リファレンスを実行
```

### with

* 指定したオブジェクトに対して複数の操作を行う

```kotlin
val ret = with(StringBuilder()) {
    append("hoge")
    toString()
}
```

### also

* 渡された変数をitで参照できるようにして連鎖できる

``` kotlin
var ret: List<String>
File("hoge.txt")
    .also {
        print(it.name)
    }.also {
        ret = it.readLines()
    }
}
```

### takeIf

* 断言（条件を定義するラムダ式）をもとに評価を行った結果が偽であればnullを返す

```kotlin
val ret = File("hoge.txt")
    .takeIf { it.canRead() && it.canWrite() }
    ?.readText()
```

### 例外

* よくある例外と同じ

```kotlin
try {
    var str: String? = null
    str ?: throw IllegalStateException() // 例外を送出
} catch (e: Exception) { // 例外を捕捉
    println(e)
}
```

### 事前条件

* 引数がnullの場合は、IllegalStateExceptionを送出する。

```kotlin
var str: String? = null
checkNotNull(str, { "str is null" }) // 事前条件
```

> 他にも、require、requireNotNull、error、assertがある

### 配列

* 配列の作成と要素の取得

```kotlin
val list = arrayOf(1, 2, 3, 4, 5)
for (item in list) {
    println(item)
}
```

### リスト

* リストの初期化と要素の取得

```kotlin
val list: List<String> = listOf("Orange", "Grape", "Apple")
list[0]
list.first() // 最初の要素の取得
list.last() // 最後の要素の取得
list.getOrElse(4) { "Unknown Fruit" } // 存在すれば要素の値、存在しなければ指定した値
list.getOrNull(4) ?: "Unknown Fruit" // 存在すれば要素の値、存在しなければnull
if (list.contains("Orange")) { // 存在確認
    plintln("OK")
}
if (list.containsAll(listOf("Grape", "Apple"))) { // 複数の存在確認
    plintln("OK")
}
```

* 要素の追加と削除（Mutable）

```kotlin
val list = mutableListOf("Orange", "Grape", "Apple")
list.remove("Orange") // 要素の削除
list.add("Banana") // 要素の追加
list.add(0, "Pear") // 挿入場所を指定した要素の追加
list.clear() // 要素をクリア
```

* コレクションの相互変換

```kotlin
val readonlyList = list.toList() // Listに変換
val distinctList = list.distinct() // 重複を排除（内部的にはtoSet()+toList()）
```

* リストの分解（5つまで）

```kotlin
val list = mutableListOf("Orange", "Grape", "Apple")
val (orange, _, apple) = list // 1つ目と3つ目の要素を取得
```

### セット

* セットは要素の重複を許可しない

```kotlin
val set = mutableSetOf("Orange", "Grape", "Apple")
set.elementAt(2) // 要素の取得
set.remove("Orange") // 要素の削除
set.add("Banana") // 要素の追加
```

### マップ

* キーと値をペアで登録

```kotlin
val map = mapOf("Orange" to 100, "Grape" to 200, "Apple" to 150)
var orange = map["Orange"]
```

* 要素の取得

```kotlin
val map = mapOf(Pair("Orange", 100),
    Pair("Grape", 200),
    Pair("Apple", 150))
map["Orange"]
map.getValue("Grape") // キーを指定して要素の取得
map.getOrElse("Banana") {"NG"} // 指定したキーの要素が存在しなければ指定した値
map.getOrDefault("Banana", 180) // 指定したキーの要素が存在しなければ既定の値
```

* 要素の追加と削除（Mutable）

```kotlin
val map = mutableMapOf("Orange" to 100)
map.put("Grape", 200) // 要素の追加
map.getOrPut("Apple"){150}  // 要素がなければ追加して取得
map.remove("Orange") // 要素の削除
```

### 繰り返し（for）

* 繰り返す回数を指定するのではない

```kotlin
for (num in 1..10) {
     println(num)
}
```

### 繰り返し（forEach）

* C#のForEachメソッドと同様

```kotlin
val list = listOf("Orange", "Grape", "Apple")
list.forEach {
    fruit -> println("$fruit")
}
```

* 要素のインデックスも取得できる

```kotlin
val list = listOf("Orange", "Grape", "Apple")
list.forEachIndexed {
    index, fruit -> println("${index+1}:$fruit")
}
```

### 繰り返し（while）

* do～whileもあり

```kotlin
var isLoop = true
var isBrake = true
while (isLoop) {
    if (isBrake) {
        break
    }
}
```

### クラス

* プライマリコンストラクタの引数にvar/valを指定すると自動でプロパティ化される

```kotlin
open class Animal(_name: String,
                    var isAlive: Boolean) {
    private var name = _name
        get() = field // ゲッター
        set(value) {
            field = value.trim() // セッター
        }

    val capitalizedName // 算出プロパティ
        get() = name.capitalize() // プライマリコンストラクタの引数を利用できる

    open fun cry() = println("hoge") // オーバーライド可能

    public fun move(distance: Int = 1) =
        if(isAlive) {
            println("The $name has moved $distance kilometers.")
        } else {
            println("The $name is dead.")
        }

    constructor(_name: String) : this(_name, true) {
        // セカンダリコンストラクタ
    }

    init {
        // 初期化ブロック（イニシャライザ）
        // プライマリコンストラクタの引数を利用できる
    }
}
```

### 継承

```kotlin
class Cat : Animal("cat", true) {
    override fun cry() = println("mew")
}
```

### インターフェース

* インターフェースの定義

```kotlin
interface Walkable {
    var strideLength: Int
    fun walk()
}
```

* インターフェースの実装

```kotlin
class Animal() : Walkable {
    override var strideLength: Int
        get() = field
        set(value) {
            field = value
        }

    override fun walk() = plintln("$strideLength walk")
}
```

### データクラス

* equals、hashCode、toString、copyが自動生成される

```kotlin
data class Point(val xpos: Int, val ypos: Int) {
    val isInBounds = xpos >= 0 && ypos >= 0
    val isOutBounds = xpos < 0 || ypos < 0
}
```

### コンパニオンオブジェクト

* スタティックメソッドのようなもの

```kotlin
class Hoge {
    companion object { }
}
fun Hoge.Companion.fuga() {
    println("fuga")
}

Hoge.fuga()
```

* companion キーワードを付与すれば、オブジェクトの名前を明示せずにメソッド名やプロパティに直接アクセスできる

```kotlin
class Hoge {
    companion object {
        fun fuga(str : String) {
            println("$str")
        }
    }
}

Hoge.fuga("Kotlin")
```

* ファクトリメソッド（staticファクトリ）を実現できる

```kotlin
class User {
    val name : String

    // セカンダリコンストラクタ1
    private constructor(name: String) {
        this.name = name
    }

    // セカンダリコンストラクタ2
    private constructor(firstName: String, lastName: String) {
        this.name = "$firstName $lastName"
    }

    companion object {
        // 名前を指定してUserインスタンスを生成を生成するファクトリメソッド
        fun createUser(name: String) = User(name)
        // 姓と名を指定してUserインスタンスを生成するファクトリメソッド
        fun createFullNameUser(firstName: String, lastName: String) = User(firstName, lastName)
    }
}

val user1 = User.createUser("Tokyo Taro")
val user2 = User.createFullNameUser("Hanako", "Tokyo")
```

### 列挙クラス

* 列挙もクラス

```kotlin
enum class OS {
    WINDOWS,
    MAC,
    LINUX
}
```

### オブジェクト宣言

* スタティッククラスのようなもの
* シングルトンの実装に利用できる

```kotlin
object Log {
    fun info(text: String) = println(text)
    fun error(text: String) = System.err.println(text)
}

Log.info("Hello")
```

### オブジェクト式

* 無名クラスのようなもの

```kotlin
val monster = object : Animal() {
    override fun cry() = "Graaaaaaaa"
}
monster.cry()
```

### 型チェック

* オブジェクトがある型であるかを評価する

```kotlin
var cat = Cat()
if(cat is Animal) {
    plintln("Cat is animal.")
}
```

```kotlin
var cat = Cat()
var className = when(cat) {
    is Cat -> "Cat"
    is Animal -> "Animal"
    else -> throw IllegalArgumentException()
}
```

### 型キャスト

* オブジェクトの型を強制的にキャストする
* Anyはすべてのクラスのスーパクラス

```kotlin
(any as Animal).name = "animal"
```

### スマートキャスト

* 型チェックが成功すれば明示的な型キャストは不要

```kotlin
if(any is Animal) {
    any.cry()
}
```

### ジェネリック

* C#のジェネリックと同じ

```kotlin
class Box<T>(item: T) {
    private var value: T = item
}

val box: Box<Int> = Box<Int>(100)
```

### 拡張関数

* C#の拡張メソッドと同じ

```kotlin
fun Any.upperPrintln() = println(this.toUpperCase)
```