Kotlinに流れ着いたエンジニアがハマりがちなもの

Happy Holidays!!
24日目は食べログapp開発チームの@fdashです。よろしくお願いいたします。

## はじめに

食べログではWeb開発(Ruby)→iOS開発(Obj-c/Swift)と経験して、現在はAndroid開発(Java/Kotlin)をメインでやっています。

KotlinはSwiftやRubyと近い書き味ですが、それゆえ微妙な差が落とし穴になりやすいです。
今回は、Swift/Rubyと同じような感覚で書くとハマりがちなもの(実際僕がよくハマるもの)をご紹介します。

## 検証環境

言語|バージョン
---|---
Kotlin|1.3.11
Swift|4.2.1
Ruby|2.5.3

## `Iterable#first`が例外を投げる


APIのレスポンスをViewModelに変換する時にめちゃくちゃハマったやつ。

**Kotlin**

```Kotlin
val hoge = listOf(1, 2, 3)

hoge.first{ it == 4 } // => NoSuchElementException！！
hoge.firstOrNull { it == 4 } // => null 本当はこっちを使わなければいけなかった
```
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/first.html
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/first-or-null.html

一致する要素が見つからなければ`null`だろうと思っていたら、まさかの例外を投げられます。


**Swift**

```swift
[1, 2, 3].first{ $0 == 4 } // => nil
```

**Ruby**

(書いてて気づいたが`#first`じゃなかった)

```ruby
[1, 2, 3].find{|e| e == 4} # => nil
```

Swift/Rubyは`nil`を返します。

Kotlinで該当する要素が無ければ何も表示しなくていいやという軽い気持ちでで`#first`を使うと速攻クラッシュします。
表示がおかしいとかではなくて落ちます。



## `Any?#toString`が`"null"`という文字列を返す

オブジェクトを文字列化してtextViewにセットする時によく間違える。

**Kotlin**

```kotlin
var hoge: Any? = null

hoge.toString() // => "null" まさかの文字列！！
hoge?.toString() // => null 期待する動作はこっち
hoge?.toString() ?: "" // => "" もしくはこっち
```

https://github.com/JetBrains/kotlin/blob/v1.3.11/core/builtins/native/kotlin/Library.kt#L25 (ドキュメントが見つからなかったので定義を張っておきます)

**Ruby**

```ruby
nil.to_s # => ""
```

Rubyは空文字を返してくれます。

つい軽い気持ちで`toString`しがちですが、アプリ上に`"null"`というTextViewが現れるとすごく悲しい気持ちになります。
ちなみに、Androidアプリ開発において`null`を`"null"`という文字列に変換したいケースが全く思いつかないため、見つけたら多分バグじゃないかな。

## `#let`は必ず評価される。

Swiftの`if let`と似ているのでうっかりしがちですが、Kotlinだとあくまでも`let`は関数です。

**Kotlin**

```kotlin
var hoge: Int? = null
hoge.let {
  // このブロック内は必ず評価される。hogeがnullであっても。
  // itとhogeはInt?のまま
}

// 期待していたのはこっち(もしくは普通にif式でどうぞ)
hoge?.let {
  // hogeがNonNullの場合のみこのブロック内が評価される
  // SmartCastされるのでitとhogeはIntとして扱える
}
```

Kotlinだとif式でもSmartCastが効くので、↑のケースだと素朴にif式で書けばよいと思います。
軽い気持ちで`let`してはいけない。
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.html

**Swift**

```swift
var hoge: Int? = nil
if let hoge = hoge {
  // hogeが非Optionalの時だけ評価される
}
```
Swiftはあくまでも`if`との組み合わせ

**Ruby**

```ruby
hoge.yield_self do
  # 必ず評価される
end

hoge&.yield_self do
  # hogeがnilなら評価されない
end
```

あまり使わないかもしれませんが、Rubyの`Kernel#yield_self`もKotlinと同じ挙動です。
あと、明日リリースされるであろうRuby2.6では`Kernel#then`というエイリアスが追加されるみたいです :tada: 

https://docs.ruby-lang.org/ja/latest/method/Object/i/yield_self.html
https://bugs.ruby-lang.org/issues/14594


## 細かいシンタックス違いシリーズ(Kotlinだけ異なるパターン)

シンタックスエラーになるので困りはしないが、つい時間を無駄にするやつたちをまとめてご紹介。

### ifの括弧

Kotlin: `if (hoge) // ( )必須`
Swift: `if hoge`
Ruby: `if hoge`

これが一番時間を無駄にするやつ。

### null/nil

Kotlin: `null`
Swift: `nil`
Ruby: `nil`

`Unresolved reference: nil`と表示されるまで気づかない。

### this/self

Kotlin: `this`
Swift: `self`
Ruby: `self`

これもまぁ。。

### 名前付き引数

Kotlin: `hogeFunc(hoge = 123, fuga = "abc")`
Swift: `hogeFunc(hoge: 123, fuga: "abc")`
Ruby: `hoge_func(hoge: 123, fuga: "abc")`

まだ慣れない。

### List/Array

Kotlin: `listOf(1, 2, 3)` / `mutableListOf(1, 2, 3)`
Swift: `[1, 2, 3]`
Ruby: `[1, 2, 3]`

Mutable/Immutableがあるから仕方ないけれど。

### Map/Dictionary

Kotlin: `mapOf(1 to "hoge", 2 to "fuga")`
Swift: `[1: "hoge", 2: "fuga"]`
Ruby: `{1: "hoge", 2: "fuga"}`

独特。。。

## 分かっていても間違えてしまうときの対策

僕はどの言語も基本的にJetBrains製IDEを使っていて操作感が変わらないため、しばしば自分が今どの言語を書いているのかわからなくなります。。。
その対策としては、IDEごとにThemeを変えて自分が今どの言語を書いているか把握しやすくしています。

|Android Studio|AppCode|RubyMine|
|:--:|:--:|:--:|
|Material Oceanic<br>(Material Theme UIプラグインを利用)|Light<br>(標準の白テーマ)|Darcula<br>(標準のダークテーマ)|
|<img width="300" alt="androidstudio.png" src="https://qiita-image-store.s3.amazonaws.com/0/145534/8ddd286b-61a8-b598-916a-c2a5ded8770c.png">|<img width="300" alt="appcode.png" src="https://qiita-image-store.s3.amazonaws.com/0/145534/ba783836-27ee-50eb-ca7f-de2a4bb2de81.png">|<img width="300" alt="rubymine.png" src="https://qiita-image-store.s3.amazonaws.com/0/145534/9ecd4109-b1e0-f5f4-66ae-efc06f6fe185.png">|

これで多少は凡ミスが減った(気がします)。

## 大事なこと

* 他の言語と似ているものほど気をつける
* 自分が今どの言語を書いているのか意識出来る環境にしておく
* **軽い気持ちでコードを書かない**

現場からは以上です。

明日、2018年のAdventCalendarを締めくくるのは、@kamina_zzzさんによる「Ruby のメモリについて」です。お楽しみに！