Javaで書いたGUIツール(TKoolFacetileMaker2)をKotlinで書き直した
去年くらいからやりたいとずっと思ってたやつをついにやった
改修内容の全体はこれ
移行したツールはこれ
背景
Androidでも正式採用されたりと、勉強しといて損はない言語だと思ってた Null安全
暗黙の型変換が無い
これがホントに大事。暗黙の型変換は数値演算周りで分かりにくいバグにつながったりする
データクラス
名前付き引数
など
今後も改修が続く可能性が高いので、移行しておくと今後の保守性が高まることを期待して
やったこと
多少はkotlinっぽい書き方に変更したりはしたけれど、ロジックには変更を加えず テストコードの修正
kotlinは暗黙の型変換が無いので、よしなにJavaが型変換してくれてた関係でパスしてたテストがコケるようになった double ←→ intの型違いのテストが大半
移行の進め方
複数のクラスから参照されてない場所から移行した
プログラムの起点になるMainクラス
Mainクラスからロードされてるコントローラクラス
コントローラからロードしてるステージクラス
これらはテストコードが無い+1箇所からしか呼ばれてないので移行しやすかった
書き直しは普通に手書きで書き直した
Intellijとかだと移行するツールがあるらしかったけれど、言語の勉強も兼ねたかったので手で書き直した ちょっとずつ移行した
javaとkotlin混在のタイミングがあったけれど許容した
動作確認するときのテスト範囲を限定するためにこうした
バグが見つかった時にロールバックも簡単にできるように
といってもmasterブランチにマージしてもタグを切らなかったら新バージョンはリリースされないので、バグがある状態でマージしてもユーザ影響はないんだけどね
移行する時はテストコードと一緒に移行した
モデルクラスとユーティリティクラスはテストカバレッジ90%以上までテストしてある
Kotlinに移行するときに一緒にテストコードもKotlinに移行した
コードフォーマッタを入れた
これがkotlinにも対応していたので、kotlin用の設定を追加した code:build.gradle
spotless {
java {
googleJavaFormat('1.13.0')
trimTrailingWhitespace()
endWithNewline()
removeUnusedImports()
}
// ここを追加した
kotlin {
ktfmt('0.15')
}
groovyGradle {
target '*.gradle'
greclipse().configFile("$rootDir/greclipse.properties")
}
}
これだけで自動フォーマットされる。快適
コーディング
普通にコード補完が効くのでサクサク書けて便利だった
コード行数
移行前と移行後
table:code
言語 行数
Java 3038
Kotlin 2612
14%ほどコード行数が減った
名前付き引数やgetter/setterをいちいち書かなくて良いのが出たと思う
戸惑ったこと
デフォルトアクセススコープの違い
Javaはデフォルトでは同じパッケージ内からのアクセスを許可する
Kotlinはデフォルトではpublic
forEachとかの内部が無名関数ではない
KotlinでforEachやlet内での早期returnはそのブロック内のreturnではない
同じことをやりたければ無名関数でラップする必要がある
はじめコンパイルエラーになってびっくりした
let, also, apply, withの使い分け
基本letとapplyしか使わなさそうな感じはしつつ
暗黙で使われる変数と、スコープの戻り値が違うという理解
リストの長さを取るのが.size()ではなく.size
Javaで良くわからなくなるあれ
table:property
言語 Stringの長さ 配列の長さ リストの長さ
Java lengthメソッド lengthフィールド sizeメソッド
Kotlin lengthプロパティ sizeプロパティ sizeプロパティ
staticが無い
staticメソッドを置き換えるためにパッケージトップレベルに移したりした
JUnit5の@AfterAll指定をしたメソッドはstaticじゃないとダメで、でもstaticがないKotlinでの書き換えの工夫が必要だったりした 感想
移行するのは難しくなかった
ユーザに配布するためにカスタムJREとか使ってたけれど、その辺まったく影響でなくて驚いた どっかコケるんじゃないかと心配してた
javaとKotlin共存しても普通に動くし、ちょっとずつ移行するとか、新しく書くクラスだけKotlinにするとか普通にできる
テストコードもガチガチに整備してたので、移行に踏み切る決断ができた。自動テストは正義
124個の単体テストに守られているので、書き直した関係でテストがコケたりしてもすぐに気付ける
ロジックが集約されてるモデルクラスはテスト網羅率90%以上あるので安心感がある
実装してたときは泣きながらテスト書いていたけれど、今になって助かった
↓テストコードが0だったので必死こいてテストコードを追加した時
javaが楽しくないわけじゃないけれど、リッチな機能がいっぱいあると楽しいね
実際コード行数は減ったし
Null安全を言語レベルでサポートしてるのでOptionalクラスをロードしなくてよかったり
let, apply, useとかで色々楽できる
getter, setterをいちいち書かなくてよい
custom getter/setterで独自のsetterを定義できる
lateinitで初期化を遅らせられる
リスト操作(map, filter)がやりやすい
変数の文字列埋め込み、RawStringリテラル
名前付き引数
使いすぎると危ない感じはある
このコードとかkotlinの機能をフルに使っててヤバさを感じる https://gyazo.com/dc1f0aa14d92c2dfdb27cff0ec522882
今後
今回は移行する方を優先したのでJavaっぽい書き方が残ってる
Kotlinっぽさをもっと追求していきたい
他にもJavaで作ったツールがいくつかあるので、全部Kotlinに移行したい
可読性の高いコーディングルールを学ぶ
言語のアップグレードを自動化する
余談
数年前にも一回新規にJavaFXアプリをkotlinで書いたことがあったけれど、数年書いてないだけで言語仕様とか綺麗サッパリ忘れてしまってた リポジトリもあるんだけれど、今回は全くそれは見ないで書いた