最初に押さえておきたいKotlin言語仕様


Kotlinをはじめるにあたって
言語仕様でここは押さえておこう、的な記事です。

普段の開発をJavaからKotlinへ移行していく過程で
これは足りない、と気づいたら随時追記します。


# 何からはじめればいいですか
Kotlin https://kotlinlang.org/
ここを起点に。本記事では主にAndroid開発を想定しています。

## Try Kotlin
ブラウザ上でサンプルを見ながら、その場でコードを書いて動作確認ができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76483/5424623d-f9e2-47d5-e5ee-42511f9915ae.png)

- コンソール↓の Open in Playground → からエディタが開きます。
- More examples からはカテゴリごとにサンプルコードが見れます。


## Kotlin Language Documentation
英語ですけどコードは読めるのでなんとなく意味はわかります。
https://kotlinlang.org/docs/kotlin-docs.pdf
或いはこちらの記事が日本語だし読みやすいかも
https://dogwood008.github.io/kotlin-web-site-ja/docs/reference/

# 地味なとこ
- 文末のセミコロンが要らない
- 拡張子 .kt

# val と var の違い
- val：読み込み専用（Getterのみ）
- var：書き換え可能（Getter,Setter）


## 定数（const）
constを付けます。アンダースコア区切りの大文字で！

```kotlin:kotlin
const val MAX_COUNT = 8
```

# Null-safety

## null-type と non-null-type
NULLチェックしてないコードはコンパイル段階でエラー
できるだけnon-null-typeを使う。
必要な場合のみnull-typeで。

```kotlin:non-null-type
var a: String = "abc"	// non-null type
a = null				// 代入だめ。ぜったい。エラー
val l = a.length		// ok
```
```kotlin:null-type（？をつける）
var a: String? = "abc"	// null type
a = null				// null代入できる
val l = a.length		// nullチェックしてない。エラー
```

## nullなら実行しない安全呼び出し

```java:java
Thread thread = null;
if(thread != null)
    thread.start();
```

```kotlin:kotlin
val thread: Thread? = null
thread?.start()
```


## nullの代替値
nullじゃなかったら設定、のような場合
エルビス演算子?:を使って完結にできます。

```java:java
if(name != null){
    textView.setText(name);
}else{
    textView.setText("no name");
}
```

```kotlin:kotlin
textView.setText(name?: "no name");
```

```kotlin:kotlin
// エルビス演算子おまけ。dataがnullになるならreturnする
val data = Hoge.getData ?: return
```

```kotlin:kotlin
// 色を取得できなければ透過色を返す例
fun getColor(@ColorRes color: Int): Int {
    return activity?.resources?.getColor(color) ?: Color.TRANSPARENT
}
```


## 非null表明
null-typeをnon-null-typeに強制変換しますが使わないほうがいいかも。

```kotlin:kotlin
fun length(str: String?) : Int = str!!.length

〜中略〜

length("hoge") // ４を返す
length(null)   // NullPointerExcelption
```

どうしてもな場合はrequireNotNull関数でメッセージを残すらしいです。

```kotlin:kotlin
val a: Int? = null
val b: Int? = requireNotNull(a) {"ぬるぽ"}  // 第二引数の文字列は任意
```


# 文字列 string
ダブルクォーテーションで囲うのは一緒
文字列中に変数を埋め込む（String Template）場合は$マークを付けて、｛｝で囲う

```kotlin:kotlin
val name: String = "ssuzaki"
println("${name}")
```

型を類推できる場合は更に｛｝も省略可能

```kotlin:kotlin
println("$name")
```

## raw string
改行を含めて書いた通りの文字列を表現する場合はダブルクォーテーションを３っつで囲う
行頭の|は目印の記号で、それが結果に含まれないようにtrimMarginしている

```kotlin:kotlin
fun main(args: Array<String>) {
    val text = """
        |むかしむかし
        |あるところに
        |じーさんばーさんが
        """.trimMargin()
    
	println("$text")
}
```

# 関数 function
```java:java
int sum(int a, int b){
	return a + b;
}
```
```kotlin:kotlin（４パターン）
fun sum(a: Int, b: Int): Int{
    return a + b
}

fun sum(a: Int, b: Int) = a + b

fun sum(a: Int, b: Int): Unit {  // 何も返さないよ
    print(a + b)
}

fun sum(a: Int, b: Int) {  // Unitは省略できる
    print(a + b)
}
```

## デフォルト引数
CustomViewを作る時なんかにオーバーロードが減るのでいいですね

```java:java
public class HogeView extends ImageView {
	public HogeView(Context context) {
		super(context);
	}

	public HogeView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public HogeView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
	}
}
```

```kotlin:kotlin
open class HogeView : ImageView {
    constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
    : super(context, attrs, defStyleAttr) {
    }
}
```

## 名前付き引数
引数名を使って関数呼び出しすると似たような関数を減らせます

```kotlin:kotlin
fun addData(name: String, age: Int = 0): String {
    return "$name + $age"
}

val result1 = addData(age = 20, name = "hoge")
val result2 = addData("hoge", age = 30)
val result3 = addData("hoge")
```

## 可変長引数
可変長にしたい引数をvarargで修飾すると配列として扱われる
１つの関数で１つまでしか定義できない

```kotlin:kotlin
fun main(args: Array<String>) {
    println( sum(1,2,3) )
    println( sum(*intArrayOf(1,2,3)) )  // 配列を渡す場合は*が必要
}

fun sum(vararg items: Int): Int {
    var total = 0
    for(item in items) {
        total += item
    }
    return total
}
```

## 再帰
[参考](https://dogwood008.github.io/kotlin-web-site-ja/docs/reference/functions.html)

```kotlin:kotlin
// これが
fun sum(num: Int): Int {
    if (num != 0)
        return num + sum(num - 1)
    else
        return num
}

// こう書ける。tailrecで修飾すると再帰が最適化されます。詳しくは参考リンク↑
tailrec fun sum(num: Int, total:Int = 0) : Int
    = if(num != 0) sum(num - 1, total + num) else total
```

## ローカル関数 local function
関数内関数です。スコープがその関数内に限定されます。

```kotlin:kotlin
fun main(args: Array<String>) {
    println( unko(true) )
}

fun unko(isHoge: Boolean): String {
    fun hoge(): String = "hoge"
    if(isHoge) return hoge() else return "unko"
}
```

## 値を返さない関数
javaでいうvoid？　Unit型の関数は値を返さなくてもいいそうです

```kotlin:kotlin
class Hoge() {
	private var cnt = 0
    fun count(): Unit {
        cnt ++
    }    
    fun count2() {	// Unitは省略するのが普通ぽい
        cnt ++
    }
}
```

## 関数オブジェクト
関数名の頭に::を付けると関数オブジェクトが取得でき、関数として呼び出せます

```kotlin:kotlin
fun main(args: Array<String>) {
    // 型を類推
	val function = ::unko
    println( function() )
    
    // 型を明示
    val function2: () -> String = ::unko
    println( function2() )
}

fun unko(): String {
    return "unko"
}
```

## ラムダ式
関数を定義せず、直接関数オブジェクトを生成します

```kotlin:kotlin
fun main(args: Array<String>) {
    val square: (Int) -> Int = { i: Int -> i * i }  // ラムダ式
    val square2: (Int) -> Int = { i -> i * i }      // 型推論
    val square3 = { i:Int -> i * i }                // 型推論
    val square4: (Int) -> Int = { it * it }         // 引数が１つの場合はitの名で変数が使える
}
```


# 制御文 Control Flow

## if
ifは式で値を返します。なので三項演算子はありません。

```kotlin:kotlin
// 普通に書けます。
var max = a
if (a < b)
    max = b

// elseも書けます。
var max: Int
if (a > b)
    max = a
else
    max = b

// 式
val max = if (a > b) a else b

// 分岐はブロックにできます。ブロックの最後の式が値になります。
val max = if (a > b) {
	print("Choose a")
	a
} else {
	print("Choose b")
	b
}
```

## when
switchみたいなやつの強化版。とてもお世話になりそう。
引数ありなしどっちでもいける。分岐条件に定数じゃないものも書ける。

- when(引数あり) { ... }
- when {...} 引数なし
- ==, ||, && も使える

ので、if-else,else,else...よりも見やすいからifよりこっちがいいかも

```kotlin:kotlin（引数あり版）
fun cases(obj: Any) {
    when (obj) {
        1          -> println("One")
        2,3 ->     -> println("Two or Three")
        in 4..9    -> println("Four to  Nine")
        "Hello"    -> println("Greeting")
        is Long    -> println("Long")
        !is String -> println("Not a string")
        else       -> println("Unknown")
    }
}

fun main(args: Array<String>) {
    cases(1)
    
    cases("Hello")
    
    val a: Long = 1;
    cases(a)	// whenの順番的にOneと表示されると思ったらLong
    
    cases(2)
    
    cases("test")
}
```

```text:結果
One
Greeting
Long
Not a string
Unknown
```

```kotlin:kotlin（引数なし版）
when {
    hoge.isOld() -> println("old")
    hoge.isNew() -> println("new")
}
```

## for
イテレータが提供されます。

```kotlin:kotlin
// くるくる
for (item in collection)
	print(item)

// ブロックくるくる
for (item: Int in ints) {
	// ...
}

// インデックスでくるくる
for (i in array.indices)
	print(array[i])

// インデックスと値でくるくる
for ((index, value) in array.withIndex()) {
	println("the element at $index is $value")
}

// 範囲でくるくる
for (i in 1..10) {
    println(hoge(i))
}

// downTo, step の指定
for (i in 100 downTo 1 step 2) {
    println(hoge(i))
}

// listがnullのケースも考慮したパターン１。listがnullだとforに入らない
for (i in list.orEmpty())  {
}

// listがnullのケースも考慮したパターン２。forの前に判断
list?.let {
    for (i in it) {
    }
}
```

### イテレータの自作

規定のメソッドが実装されていればイテレータを備えるクラスを自作できる

```kotlin
fun main(args: Array<String>) {
    for(item in Iterator()) {
        println(item)
    }
}

class IteratorFunctions {
    operator fun hasNext(): Boolean = Math.random() < 0.8
    operator fun next(): String = "unko"
}

class Iterator {
    operator fun iterator() = IteratorFunctions()
}
```

## while
お馴染みの前判断、後判断、どっちもいけますが
後判断のスコープが便利になってます。

```kotlin:kotlin
// 前判断
while (x > 0) {
	
}

// 後判断
do {
	val y = retrieveData()
} while (y != null) // yが見える！
```


# 範囲指定 range
```kotlin:kotlin
// ｘが１〜１0
if(x in 1..10)
	println("ok")

// ｘが１〜１0じゃない
if(x !in 1..10)
	println("ng")

// リスト
println((1..5).toList())               // [1, 2, 3, 4, 5]
println((1..5).reversed().toList())    // [5, 4, 3, 2, 1]
println((5 downTo 1).toList())         // [5, 4, 3, 2, 1]
println((1..5 step 2).toList())        // [1, 3, 5]
```


# 配列 array
```kotlin:kotlin
// お好みで
val list = arrayOf(1, 2, 3)               // 型は類推される
val list = intArrayOf(1, 2, 3)            // intで
val list: Array<Int?> = arrayOfNulls(3)   // nullな要素を３っつ

list[0] = list[1] + list[2]       // 各要素はお馴染みのカッコで

for(item in list){                // forループくるくる
    println(item)
}
```


# コレクション collection
## list（重複要素あり）
```kotlin:kotlin
val list = listOf(1, 2, 3)
list[0] = list[1] + list[2]    // immutable（変更不可）エラー

val list = mutableListOf(1, 2, 3)
list[0] = list[1] + list[2]    // mutalbe（変更可能）

val list = mutableListOf<Hoge>() // 空っぽでinitialize
```


## set（重複要素なし）
```kotlin:kotlin
val list = setOf(1, 2, 2, 3)  // immutable（変更不可）変更する場合はmutableSetOf
for(num in list)
    print(num)  // 123
```

## map（キーと値のペア）
```kotlin:kotlin
val map = mapOf(    // immutalbe（変更不可）。変更する場合はmutableMapOf
    1 to "one",
    2 to "two",
    3 to "three"
)
//for(item in map){
//    println("${item.key} to ${item.value}")
//}
for((key, value) in map)
    println("${key} to ${value}")

println(list[1])
```
```text:結果
1 to one
2 to two
3 to three
one
```


# データクラス data class
値を保持するだけのクラスならこれで。クラス名の前にdataを付けます。

```kotlin:kotlin
data class User(val name: String = "", val age: Int = 0)
```
使い方

```kotlin:kotlin
val jack = User(name = "Jack", age = 1)  // jackさん１歳
val olderJack = jack.copy(age = 2)       // jackさんの名前のまま２歳のコピーを

val jane = User("Jane", 35)  // janeさん３５歳
val (name, age) = jane       // janeさんの名前、年齢をGET！
```

copy用の関数が作られるみたい

```kotlin:kotlin
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
```

constructor を追加して使いやすく

```kotlin
    data class User(val name: String = "", val age: Int = 0) {
        constructor(name: String, age: Int, email: String) : this(name, age)
    }
```

# enumクラス
```kotlin:kotlin
enum class Direction {
    NORTH, SOUTH, WEST, EAST
}
```

定義と初期化

```kotlin:kotlin
enum class Color(val rgb: Int) {
    RED(0xFF0000),
    GREEN(0x00FF00),
    BLUE(0x0000FF)
}
```

ちょっと使いやすく

```kotlin:kotlin
enum class HogeType constructor(val value: Int) {
    AAA(0),
    BBB(1),
    CCC(2);

    companion object {
        fun fromInt(value: Int): HogeType {
            return values().firstOrNull { it.value == value } ?: AAA
        }
    }

    val isAAA: Boolean
    get() = this == AAA

    val isBBB: Boolean
    get() = this == BBB
}
```


# シングルトン singleton
```kotlin:kotlin
object SingletonUser {
    private var name: String = "ssuzaki"
    
    fun init(data: String) {
        name = data
    }
    
    fun put() {
        println("${name}")
    }
}

fun main(args: Array<String>) {
    SingletonUser.put()
}
```

# コンパニオンオブジェクト companion
staticの代わりみたいなの

```kotlin:kotlin
class MyClass {
    companion object { }
}

fun MyClass.Companion.foo() {
    println("hoge")
}

fun main(args: Array<String>) {
	MyClass.foo()
}
```


# 同一 equality
同じ参照先か===で比較。違う参照先は!==

```kotlin:kotlin
val taro = User("taro", 10)
val jiro = User("jiro", 11)

if(taro === jiro)
	println("taro equal jiro")
```


# 例外 Exceptions
例外はすべて Throwable の子孫でメッセージ、スタックトレースを持ち
オプションで原因を持ちます。

## 例外を投げる throw

```kotlin:kotlin
throw MyException("Hi There!")
```

## 例外を捕捉する try...catch...finally

```kotlin:kotlin
try {
    // some code
} catch(e: SomeException) {
    // handler
} finally {
    // optional finally block
}
```

## tryは式で値を返せる

```kotlin:kotlin
val a: Int? = try { parseInt(input) } catch (e: NumberFormatException) { null }
```


# クラス class

```kotlin:kotlin
class Hoge {
    // プロパティの定義。getterとsetterが定義される。valならgetterのみ
    var name: String = ""

    // コンストラクタ
    constructor(name: String) {
        this.name = name
    }

    // メソッドの定義
    fun changeLanguage(language: String) {
    }
}
```

## プライマリコンストラクタ

```kotlin:kotlin
class Hoge(n: String) {
    val name = n
}

// プライマリコンストラクタでプロパティも一緒に定義するケース↓
class Hoge(val name: String) {
}
```

## クラスのネスト

```kotlin:kotlin
class Hoge {
    val num: Int

    // 外側のクラスのメンバを参照するときはinner classにしないと参照できないよ
    inner class Fugo {
        val total = num + 10

        // おまけ：inner class から外側のクラスのthisを指したいときは@で指定
        // this@Hoge
    }
}
```

## 継承

```kotlin:kotlin
// openを付けないと継承できない
open class Base {
}

class Hoge : Base() {
}
```


# プロパティ property properties
引数のない戻り値だけの関数はプロパティとして書いたりするといいかも？
書き方も何パターンかあってgetter or setter またはその両方で用意できます。

```kotlin:kotlin
// getterだけ書くパターンいろいろ
val isEnable: Boolean = hoge()
val isEnable: Boolean
    get() = hoge()
val isEnable: Boolean
    get() {
        if (hoge())
            return true
        else
            return false
    }

// setterも用意する場合（varで定義してね）
var isEnable: Boolean = false
    set(value) {
        field = value  // fieldはプロパティのこと。この例では単純に代入
        Hogehoge()     // 代入をトリガーに何か処理をしてもいいし
    }

// setterをprivateにする場合
var isEnable: Boolean = false
    private set
```


# インターフェイス interface

```kotlin:kotlin
interface Hoge {
    fun hoge()
}

// javaでのimplementsは:で
class Sample: Hoge {
    override fun hoge() {
        println("gorua-")
    }
}

// 無名クラスで実装↓
class Sample {
    private val h = object: Hoge {
        override fun hoge() {
            println("gorua-")
        }
    }
}
```


# キャスト cast
as で

```java:java
Hoge hoge = (Hoge) obj;
```

```kotlin:kotlin
val hoge = obj as Hoge
```


# スコープ関数 Scope Functions

使い分けが難しく感じるので使いそうな例だけ

## let　（nullじゃなかったら○○したい）

null-typeの変数と一緒に、nullじゃなかったら○○したいシチュエーションで

```kotlin:kotlin
// これが
if (hoge != null) {
    textView.text = hoge.name
}

// こう書けます
hoge?.let {
    textView.text = it.name
}
```

```kotlin:kotlin
var list: MutableList<Int>? = null
list?.let {
    println(it)  // nullなので出力されない。クラッシュしないよ。
}

list = mutableListOf (1, 2, 3)
list?.let {
    println(it)  // nullじゃないので出力される。
}
```

## with　（編集中）


## run　（編集中）


## apply
- apply元を何度も書かずに済ませたい
- apply { } 中はapply元に対する何かを行う
```kotlin:kotlin
// before
binding.textView1.text = "aaa"
binding.textView2.text = "bbb"

// after
binding.apply {
    textView1.text = "aaa"
    textView2.text = "bbb"
}
```


## also　（編集中）


# 遅延初期化

## lateinit
- 後から初期化するので許してください！みたいな
- isInitialized で初期化されたか判断はできるっぽい
- あまり多様すると初期化される前に触られてクラッシュするので利用は控えめがいいかも

```kotlin:kotlin
class HogeView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0): ConstraintLayout(context, attrs, defStyleAttr) {

    // あとで必ず！必ず初期化しますから！今はご勘弁を！
    lateinit var binding: HogeViewBinding

    init {
        if (isInEditMode) {
            // レイアウトエディターとかでプレビュー中はこっちなのでbindingしないよ
            inflate(context, R.layout.hoge, this)
        } else {
            val inflater: LayoutInflater = LayoutInflater.from(context)
            binding = DataBindingUtil.inflate(inflate, R.layout.hoge, this, true)
        }
    }
}
```

## by lazy
- プロパティーとかではじめてアクセスしたときに初期化されるようお願い
- by lazyされたプロパティーはインスタンス作成時はまだ初期化されてません

```kotlin:kotlin
class Hoge {
    // hogeインスタンス作ったときはまだ初期化されておらず
    // はじめてnameプロパティにアクセスしたときに委譲先のgetName()で初期化される
    // ２度目以降は↑の値を返すので、何度もプロパティーの値を作るのに時間がかからない
    val name: String by lazy { getName() }

    private fun getName(): String {
        // ここでなにかとてつもない重い処理があったりして
        return "hoge"
    }
}
```