Kotlin使ってみて感じる便利さと葛藤
最近アプリやAndroid Studio用プラグインを作るのにKotlinを使っています。
始めたばかりの頃は「Javaだとああ書くんだけど、Kotlinだとどう書けばいいんだ」ということが多かったです。Javaでまともに書けないのにKotlinに手を出すのは早いんじゃないかとも思っていました。
しかし少しずつ試していくと、Kotlinの便利な部分が分かってきてきました。
私の場合、「Kotlinで書き始めたんだけどやっぱ使い方よくわからないからJavaに戻そう」とう場面が初期の頃はよくありました。はじめはKotlinで書いていたけど、やっぱりJavaで実装しようとという感じです。
そんなときに、「Javaに戻すのめんどくせえ」と感じる部分があって、そこで改めて「Kotlinってやっぱ便利やなぁ」なんて実感しました。
それからというもの、Kotlinの比重が徐々に増えてきて、今では逆にJavaで書く方が面倒くさいと感じるようになってしまいました。
一方で、Kotlinが無敵というわけではありません。Annotation Processingを使うライブラリがKotlinだとうまく使えないことがあったり(基本的には大丈夫ですが、Javaで書けば動くコードがKotlinで同じように書くと動かないことがあったりします)、Javaと比べるとコード補完が遅かったり、Kotlinを使うことで感じるストレスもあります。
ですが、不便さを差し置いてもKotlinで書いた方がすっきり書けるのはやっぱり快適だと思っています。
どなたかの記事で、Kotlinはテストコードから導入してみたらどうかという記事を読みました。私もいい方法だと思います。Kotlinの便利さを実感するためではなく、どう書くかを知るのにちょうどいいと思います。
私もJUnitでのユニットテストをKotlinで書いています。テスト対象をKotlinで書いてるからとか、セミコロンつけなくてもいいから、とかそんな理由です。ユニットテストについてはKotlinが便利だからという理由はあまりないかもしれません。
ユニットテストにおいてKotlinが便利だと思うのは、バッククオート(`)で囲むことで、メソッド名やクラス名を数字から始めることができたり、途中に空白を含めることができたりすることでしょうか。
私はテスト名に日本語を使うことが多いです。そしてそのときに、メソッド名に使える文字に制約があるのが微妙に困ります。
例えば各月の最終日を求めるメソッドのテストをするのに「4月の場合は30日を返す」というテスト名にしたくてもできません。Javaではメソッド名を数字から始めることができないからです。だからこんなときは「月に4月を指定したら30日を返す」という感じのメソッド名にするのですが、これが微妙なストレスになります。(頭に「月を」つけるだけやんという感じですが、微妙にストレス感じるんですよこれ)
Kotlinではこの制約に煩わされることがありません。メソッド名をバッククオートで囲めば、数字から始めようが、途中に空白を挟もうが問題ないのです。
@Test fun `2つの時刻の差を求める`(){
val time1 = LocalDateTime.of(2014, 1, 1, 23, 58, 30)
val time2 = LocalDateTime.of(2014, 1, 2, 0, 4, 30)
val actual = Duration.between(time1,time2).seconds
assertThat(actual, `is`(360L))
}
なにそれ気持ち悪いと思われるかもしれませんが、これはれっきとしたKotlinの仕様です。
Javaのメソッド名規約によってテストメソッド名を考えるのが面倒くさいなぁと感じている人は私だけではないと思いたい。
一方でKotlinでユニットテスト書けば便利なことばかりではありません。例えばassertThat
などを使おうとするとimport文を手書きで書かないと認識してくれないのが不便です。(私の環境の問題なのかもしれません。JavaだとALT+Enterでimportできるんですけどね・・・)
単純なことですが、Kotlinは文末にセミコロンをつけなくてもいいのです。
これが便利・・・と言いたいところですが、私は半々かなぁと感じています。
確かにいちいちセミコロンつけなくてもいいので楽です。たまにJavaでコードを書くときに、しょっちゅうセミコロンつけ忘れます。それくらいには快適です。
一方セミコロンが不要なせいで、メソッドチェーンするときに私は微妙にストレスを感じます。
Javaだとメソッドチェーンするときに改行をするとインデントを一段深くしてくれます。
しかしKotlinでは改行した時に文末なのか次の文に移るのかが判別不能なので、インデントを深くしてくれたりしません。これが毎回微妙にめんどうくさいです。私はいつもドットを打ってカーソル戻して改行するという方法をとって回避しています。
多分Reformat Code(Cmd+Alt+l
)を使うのが楽なんでしょうけども。みんなどうしてるんだろう・・・。
Javaだと文字列に変数を埋め込もうと思うと、+演算子で連結しなければなりません。もしくはString.format()
を使うかですね。
Kotlinだとそんな面倒くさいことをせずとも、文字列中に変数を埋め込めるので便利です。こんな感じに書けるわけです。
val hoge = "num is $num"
埋め込む変数が多くなればなるほど、これはとても便利になります。デバッグのために変数の中身を文字列として出力して確認することがよくあると思いますが、そんなときに特に楽だと思います。
複数行に渡る文字列も、"""
で囲むことでそのまま文字列として扱うことができます。ただしこれを使うと、インデントによる空白も文字列に含まれてしまうので、使いドコロが難しい気もします。
例えば配列の要素の中から最大値を取得しようと思ったら、Javaだとこんな感じになるでしょう。
int[] array = {1,20,3,40,5,16,7};
int max = 0;
for (int num : array) {
max = Math.max(num, max);
}
Kotlinだとこうです。
val array = arrayOf(1,20,3,40,5,16,7)
val max = array.max()
わざわざループを回したりしなくてすんで、非常にすっきりします。
他にも、例えば配列のインデックスもあわせて使いたい場合は、.forEachIndexed{index, data ->//処理}
なんて書けます。for文を書く頻度がだいぶ少なくなります。
一方で、配列の宣言がいまだに慣れません。
arrayOf()
、arrayListOf()
、listOf()
、mutableListOf()
などなど、いろいろ種類があっていつも混乱します。JavaのListを引数に取るメソッドをKotlinから利用するときなど、引数の型が違うという感じでAndroid Studioに怒られるのがしょっちゅうです。
Kotlinの便利さを実感するのがこの拡張関数です。
例えばorg.threeten.bp.LocalDateTimeとjava.sql.Timestampの変換をしようと思うと、DateTimeUtilsクラスを利用すればできます。
val localDateTime = LocalDateTime.now()
val timestamp = DateTimeUtils.toSqlTimestamp(localDateTime)
でもこれ、LocalDateTimeクラスに直接Timestampへの変換を行うメソッドがあったらもっとスマートに書けます。そしてKotlinなら拡張関数を利用することでそれが実現できます。
例えばUtil.ktというファイルを作成して、そこにこんな関数を定義します。
fun LocalDateTime.toTimestamp() = DateTimeUtils.toSqlTimestamp(this)
するとval timestamp = localDateTime.toTimestamp()
と書けるようになるのです。
Javaだとユーティリティクラスを作成して、staticメソッドでやっていたようなことを、Kotlinだとずっとスマートに実装することができます。これに手を出し始めるとJavaには戻れないですね。
ただ多用すると知らない人からみたら「なんぞこれ」というコードになってしまいそうです。自分で作って自分で使う分には便利で気持ちいいのですが、チームで使うときにはまた違う感想になるのかもしれません。
私自身はKotlin便利で、全部Kotlinで書けたら楽だなぁとは思っているんですが、一方で素直にJavaで書いた方が楽じゃないかと思うときも良くあります。
書き上がったソースコードはすっきりしているものの、書いてる最中はコード補完が遅くてストレス感じます。単純に私のマシンスペックの問題なのかもしれませんが(3年前に買ったMacbook Airなのでいい加減買い換えたい)、固まってイライラすることも少なく無いです。たまにJavaで書くとコード補完が早くて快適に感じます。
KotlinはJavaと100%互換で、基本的にはKotlinかJavaかを意識しなくてもいいのですが、それでもやっぱりJavaとの境界は意識せざるを得ません。Javaのことを意識して書かざるをえないのであれば、はじめからJavaで書いた方が楽という部分はあると思います。
また、Android StudioのInstant Runとの関係かもしれませんが、変更したソースコードが反映されずにデバッグに余計な手間がかかることがたまにあるのもストレスです。毎回起こるわけでもないのがまた微妙なところです。
素直にJavaで実装したほうが楽なのかもしれないけれども、一方でKotlinの便利さを知ってしまったがゆえにJavaで書くのも面倒くさいという葛藤を抱いております。ちょっと前までは「フルKotlinが一番便利でいい」とこだわろうとしていたのですが、今はKotlinとJavaをいい感じに併用していく柔軟性が大事なのかなと考えを改めているところです。