自分なりのKotlin Coding Tips

食べログのアプリDevOpsチームでAndroidエンジニアやっている @sadashi です。
最近はアプリアーキテクチャ設計やコーディング規約を考えたりしています。
[食べログ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/tabelog) 6日目の記事ではKotlinでのCoding Tipsについて書きたいと思います。

# はじめに

Kotlinでコーディングする際に気をつけた方がいいところであったり、ちょっとしたTipsを書いていきたいと思います。
私自身Androidメインなので、内容がAndroidになっているところもありますが、その点はご了承ください。

# 環境

IDEはAndroidStudioを使っていて、Kotlinは1.3.10をベースに考えています。
あと、単純にKotlinを書いてみたいなら[Kotlin Playground](https://play.kotlinlang.org)を使うとお手軽で良いですよ！

# Tips

## Lint

`lint`は必ず使いましょう。自分も気づかなかった不具合を見つけてくれるかもしれません。
また合わせて`formatter`としても使うことをお勧めします。コーディングの効率が上がり、書き方も統一されるので、可読性も高くなります。
Kotlinに対応しているものは以下のようなものがあります。

- [ktlint](https://github.com/shyiko/ktlint)
- [detekt](https://github.com/arturbosch/detekt)
- [Android Lint](https://developer.android.com/studio/write/lint?hl=ja)

## トップレベル関数, 拡張関数

頻繁には使わない方が良いと思います。ルールなしで使うとカオスになります。
使いたい場合はスコープを限定するなどを検討すると良いかもしれません。

私としては、明らかに汎用性が高いようなものはあっても良いと思っています。
Androidですが一例としては以下のようなものです。

```kotlin
// Picassoを利用した、URLからImageViewへの反映
fun ImageView.imageUrl(url: String?) {
    if (url.isNullOrEmpty()) {
        setImageDrawable(null)
        return
    }
    Picasso.with(context).load(url).into(this)
}

// ViewGroupのinflate
fun ViewGroup.inflate(@LayoutRes layoutRes: Int): View {
    return LayoutInflater.from(context).inflate(layoutRes, this, false)
}
```

## 型の明記

型は基本的には書いた方がわかりやすいと思います。
省略するとしたら、プリミティブな型かコンストラクタを直接呼ぶ場合のみがおすすめです。

```kotlin
// NG
val huga = factory.create()     // 型がよくわからない
val hugaInterface = HugaImpl()  // 抽象化したいのに略すと実装したクラスになってしまう

// OK
val primitiveInt = 0
val primitiveString = ""
val directConstractor = Hoge()
val huga: Huga = factory.create()
val hugaInterface: HugaInterface = HugaImple()
```

## Null Check

`null`チェックして`early return`する場合はエルビス演算子（`?:`）を使うと良いです

```kotlin
fun dummy(hoge: Hoge?) {
  val huga = hoge ?: return
  // ...
}
```

何か処理をさせたい場合はスコープ関数(`run` or `let`)を使います

```kotlin
fun dummy(hoge: Hoge?) {
  val huga = hoge ?: run {
    // do something
    return
  }

  // ...
}
```

逆に`null`ではない場合に処理させたければこんな感じです。

```kotlin
fun dummy(hoge: Hoge?) {
  hoge?.let {
    it.hogehoge()
  }

  // or

  hoge?.let {
    // smart cast
    hoge.hogehoge()
  }
}
```

こういった場合はスマートキャストの方がわかりやすいと思います。


## `!!` (not-null assertion operator, force unwrap)

`null`に対して行うと即クラッシュしてしまうので、基本的には利用しないようにした方が良いと思います。
`early return`を意識して、`smart cast`をするのが一番わかりやすいです。

どうしても必要なときは`requireNotNull`でメッセージをつけると、意思が伝わります。

```kotlin
val a: Int? = null
val b: Int = requireNotNull(a, {"ここでnullになってはいけないよ"})  // 第二引数の文字列は任意
```

## スコープ関数（`let`,`run`,`apply`,`also`）

ここは好みがでるところかもしれません。
個人的には`also`と`let`のみを使います。

理由としては`apply`/`run`はレシーバで`this`をとりますが、クラス内のメンバに同じ名前が存在すると意図しない挙動となるためです。

```kotlin
// NGパターン
class Hoge {
    var text = ""
}

fun init() {
    var text = ""
    val hoge = Hoge().apply {
        text = "hoge"
    }
    hoge.text // "hoge"になっていない
}
```

`this`をつければいいんじゃね？って意見もあると思いますが、それだと`it`つけるのと変わらないので、明示的に使わないという方向に倒しているだけです。

ただし、以下の例のように、初期化時の設定は`apply`の方が意味がわかりやすいとか、エルビス演算子（`?:`）の後の処理は`run`の方が直感的とか、人によって意見は色々あると思いますが、この辺りはどういったときに問題が起こるかを把握した上で議論し、しっかりと「共通認識」を持つことが大事かなと思っています。

```kotlin
// initialize
val intent = Intent().apply {      // <- 自分ルールだとalso
    it.putExtra("key1", "value1")
    it.putExtra("key2", "value2")
    it.putExtra("key3", "value3")
}

// null check
val hoge = str ?: run {     // <- 自分ルールだとlet
    // なんか処理する
    return
}
```

## Singlton

`Dagger`などのDIライブラリを利用されている方はあまり使わないかもしれませんが、シングルトンは以下のように書くといいです。

```kotlin
// Singleton
class SingeltonHoge {
    companion object {
        private var INSTANCE: SingeltonHoge? = null

        fun getInstance(): SingeltonHoge {
            return INSTANCE ?: SingeltonHoge().also { INSTANCE = it }
        }
    }
}
```

Android StudioやIntelliJなら`Live Template`に以下を登録しておくと便利です。

```
companion object {
    private var INSTANCE: $CLASS$? = null

    fun getInstance(): $CLASS$ {
        return INSTANCE ?: $CLASS$().also { INSTANCE = it }
    }
}
```

## Custom Setter

Custom Setterは変数の委譲のみに留め、他の用途ではあまり使わない方が良いかと思います。

```kotlin
private val user = User()
var userName: String
    get() = user.name
    set(value) {
        user.name = value
    }
```

もし、以下のように設定ついでに更新や通知処理などを行うと以下の問題が起こります。

- 値の更新だけをすることができない
- 他の値と一緒に更新することができないなど

```kotlin
// NG
private val user = User()
var userName: String
    get() = user.name
    set(value) {
        user.name = value
        update()
        notify()
    }
```

## Custom Getter

処理に時間がかかったり、何か状態を変えるような副作用がある場合には、Custom Getterを使わずに通常の関数を利用した方がよいでしょう。
処理に時間がかからず、副作用も特にない場合はCustom Getterを使っても問題ないと思います。

この辺の話は公式の[Kotlinコーディング規約(Functions vs Properties)](https://kotlinlang.org/docs/reference/coding-conventions.html#functions-vs-properties)にも記載があります。

## メンバ変数の初期化方法

メンバ変数を初期化する方法として以下の4種類があります。

- `lazy`
    - 最初にアクセスがあった時に値が計算されて、それ以降同じ値を返す
    - 不変のため、`val`でしか宣言できない
- `custom getter`
    - アクセスがあるたびに計算される
- 通常の代入
    - インスタンス生成時に計算される
- `lateinit`
    - 初期化されていない状態をもち、どこかで初期化される前提となる宣言
    - 初期化されていない状態でアクセスすると例外が発生する
    - 必ず初期化以外の代入が行われる前提なので`var`でしか宣言できない
    - `Nullable`は認めない

これらを理解せずに曖昧に使ってしまうと痛い目にあうので気をつけましょう。
使う場合には以下のような観点でまとめると良いと思います。

- `lazy`
    - 初期化時には値が確定しない、不変
- `custom getter`
    - 状態が変わると値も変わる
- 通常の代入
    - 初期化時に値が確定、不変
- `lateinit`
    - 初期化時には値が確定しない、利用シーンは要検討（後述します）

不変でない値については`custom getter`か通常の代入になるかと思います。
`lateinit`については後述するルール次第になります。

### `lateinit`

`lateinit`は利用箇所をルール化することをオススメします。
例えば以下のようなケースです。

- `Activity`/`Fragment`などの`onCreate()`などで確実に代入するメンバ変数
- `Dagger`などのDIライブラリを利用して`Injection`するメンバ変数

`lateinit`の変数が初期化されているかチェックする`isInitalized`がありますが、これを使うということは未初期化の状態を管理しなければならないため、`Nullable`で初期値に`null`を代入していることと変わりません。
上記のようなルール外では、`Nullable`を利用した方が混乱が少なく済むでしょう。

### `lazy`と`lateinit`の違い

どちらも遅延初期化という点は同じですが、動作は大きくことなります。

||初期化のタイミング|var / val|Nullable|
|---|---|---|---|
|lazy|初回アクセスされたとき|val|OK|
|lateinit|明示的に初期化する|var|NG|

注意点としては`View`で`lazy`を使うと`Fragment`で渡す`View`が更新されないなどの不具合に繋がるため、`View`では`lazy`ではなく`lateinit`を使ってください。


# 最後に

ここにまとめたのは色々な情報収集をした上で、自分なりに整理したものですので、まったく違ったご意見もあるかとは思います。
そういったところがあれば是非コメントいただけると幸いです。


明日の7日目は@weakbosonさんより「Rails の trusted proxy めんどいところとワークアラウンド」です。お楽しみに！