こんにちは、ゆのうえです。 近頃、主にAndroid界隈で話題沸騰中(希望的)のJVM言語Kotlin。 この度、「ファッションコーディネートアプリMANT」でもAndroidアプリ開発にKotlinを導入しましたので、既存のJavaプロジェクトにKotlinを導入してよかったことや、気をつけなければいけないことなどを2ヶ月半ほどの導入経過からお伝えしたいと思います。
プロジェクトの進め方
基本的に新規クラスはKotlinで書いています。 既存クラスも余裕があれば随時コンバートを進めていきます。 プラグインにJava→Kotlinの自動コンバート機能がありますが、問題ないと判断できる所でしか使用していません。そのままではきれいなコードになりませんし、例外が発生するリスクがあるからです。
導入してよかったこと
・null安全
Android Javaと他のJVM言語(あるいはRetrolambdaなど)の比較でよく引き合いに出されるのはラムダ式ですが、それよりももっと嬉しかったのがKotlinのnull安全性です。 たとえば、
fun printStrLength(s: String?) { println(s.length()) }
このようにNullPointerException(以降NPE)が発生しうるコードはコンパイルエラーとなり、IDEが指摘してくれます。
・s?.length()
としてnullを許容する(sがnullの場合println(null)
が実行されます)
・引数のnullチェックをする
・引数の型をStringにしてそもそもnullを許容しない(メソッドの呼び出し元でnullが入りうる場合エラーになってくれます)
などの対応が必要です。
これによってJavaで要求されていた過剰なnullチェックやそれでも発生するNPE、無駄に増えるテストケースを抑制することができます。
・やっぱり関数リテラルは凄かった
いわゆるひとつのラムダ式。の凄いやつ。 様々な場面でごっそり記述を省略することができます。
・valとvar
valキーワードで宣言したプロパティは読み取り専用の定数扱いになります。 値を基本的にvalで宣言することで精神的な安らぎが得られます。
・楽しい!
Kotlinは書き方の自由度が高い言語です。 慣れないうちはJavaっぽく書くこともできますし、びっくりするほど省略した記述にできることもあります。 新しい書き方を覚える度にコードが簡潔になっていく魅力があります。 Javaでは記述が複雑になるため実装を躊躇していたようなモデルでも、あっさり実装できるかもしれません。
気をつけなければならないこと
・null安全じゃない
いきなりよかったことを否定しにかかっていますが、これは今回のようにJavaクラスが混在のプロジェクトでは避けられない問題です。 JavaからKotlinのメソッドを呼び出す時にNPEが発生してしまうことがあるからです。 例えば
public class Hoge() { fun printStrLength(s: String) { println(s.length()) } }
というKotlinのクラスが存在するとき、
void print(String input) { Hoge hoge = new Hoge(); hoge.printStrLength(input); }
のようにJavaで記述したとしてもコンパイルは通ってしまいます! もしinputがnullだったらNPEが発生するリスクがあります。 Java側で@NotNullを付けてチェックすることもできるのですが、null安全はそんな細かい気配りのできない私のような人間のための機能。 それが使えないのは辛いです。 Javaから呼ばれる可能性のあるメソッドはnull許容型(?付き)で宣言したほうがいいかもしれません。 また、Javaのメソッドから受け取る返り値にも注意が必要です。
val extras: Bundle? = getIntent().getExtras()
このように明示的にnull許容型として宣言しておくとミスがなくなりやすいと思います。
・レビューが大変?
Javaとは違うため、コードレビューには戸惑いがあると思います。 しかし、慣れないうちはほとんどJavaに近い形で書いていたためそれほど「読めない」という問題は起きませんでした。 少しずつKotlinっぽい書き方に慣れていくに従って、レビュワーにも読み慣れてもらうしかないですが、Javaを知っていれば学習コストは低いと感じています。 同じAndroidの開発者同士でレビューをするとお互い言語理解が深まってよいサイクルができそうです。 今のところ最も激しいツッコミがあったところは、
val hoge = array(1, 2, 3) hoge.forEach { println(it) }
のような式での"it"についてです。 これは初見では分かり辛い…。 (※itについてはたろう氏のブログが参考になります)
・ビルドに失敗する
最近になってgradleでのビルドに失敗することが増えました。 リソースIDの更新がされないため再ビルドを行ったり(再ビルドが失敗することも…)、メモリ不足が発生したためメモリの上限を上げたりという対応をしています。今後プラグインのアップデートで解決して欲しいところです。
まとめ
Kotlinかわいい。
他にもwhen式や拡張関数など、役に立つ機能はたくさんあります。導入を快諾してくれたチームと会社に感謝。