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
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
[1, 2, 3].first{ $0 == 4 } // => nil
Ruby
(書いてて気づいたが#first
じゃなかった)
[1, 2, 3].find{|e| e == 4} # => nil
Swift/Rubyはnil
を返します。
Kotlinで該当する要素が無ければ何も表示しなくていいやという軽い気持ちでで#first
を使うと速攻クラッシュします。
表示がおかしいとかではなくて落ちます。
Any?#toString
が"null"
という文字列を返す
オブジェクトを文字列化してtextViewにセットする時によく間違える。
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
nil.to_s # => ""
Rubyは空文字を返してくれます。
つい軽い気持ちでtoString
しがちですが、アプリ上に"null"
というTextViewが現れるとすごく悲しい気持ちになります。
ちなみに、Androidアプリ開発においてnull
を"null"
という文字列に変換したいケースが全く思いつかないため、見つけたら多分バグじゃないかな。
#let
は必ず評価される。
Swiftのif let
と似ているのでうっかりしがちですが、Kotlinだとあくまでもlet
は関数です。
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
var hoge: Int? = nil
if let hoge = hoge {
// hogeが非Optionalの時だけ評価される
}
Swiftはあくまでもif
との組み合わせ
Ruby
hoge.yield_self do
# 必ず評価される
end
hoge&.yield_self do
# hogeがnilなら評価されない
end
あまり使わないかもしれませんが、RubyのKernel#yield_self
もKotlinと同じ挙動です。
あと、明日リリースされるであろうRuby2.6ではKernel#then
というエイリアスが追加されるみたいです
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 (Material Theme UIプラグインを利用) |
Light (標準の白テーマ) |
Darcula (標準のダークテーマ) |
これで多少は凡ミスが減った(気がします)。
大事なこと
- 他の言語と似ているものほど気をつける
- 自分が今どの言語を書いているのか意識出来る環境にしておく
- 軽い気持ちでコードを書かない
現場からは以上です。
明日、2018年のAdventCalendarを締めくくるのは、@kamina_zzzさんによる「Ruby のメモリについて」です。お楽しみに!