Java歴はそれなりにあるのですが最近はJavaよりPHPを書いていたり、またAndroidアプリ開発をしないのでKotlinとは無縁の生活をしておりました。
ちょうどいい感じのアプリ規模(1人で設計・実装・テストで10日未満)の案件があったので久々にJavaで開発することにしてどうせならということでKotlinで実装することにしました。
基本的なアプリ周りのインフラは以下の通り
- 社内にKotlin経験者はいない
- IDEはIntelliJ IDEA Ultimateを使う
- SpringBootを使う(この規模ならもっと軽量なDIコンテナのほうがよいかも・・・)
- DBアクセス系はDoma2を使う
DBアクセス系ライブラリ
最近PHPというかLaravelを触っていてO/Rマッパー便利だなーと思う一方で複雑なSQLになると、すごいもどかしい気持ちになったりしております。
JavaでO/Rマッパーとなると数が限られていてHibernateとかになるのかなーと思います。
そして悩ましいのもここ。
O/RマッパーではないかもしれませんがSQL文を書けるMyBatisやタイプセーフなJOOQなどもありますしSpringBootにインテグレーションが準備されているので簡単に使いはじめれます。
最近はDomaを使っていてMyBatisなども使いましたが自分にある程度裁量がある場合は基本的にDomaを使っています。
自分がDBアクセス系ライブラリに求めるのは
- Select文を書けること
- Insert文, Update文, Delete文を書かなくてもよいこと(自動生成してくれる or 自動実行してくれる)
- SQL実行結果をJavaのオブジェクトにマッピングできること
みたいな感じです。
ちょうどこのアプリケーションKotlinで書こうと思っていたところQiitaでKotlin, SpringBootk, Domaという奇跡のようなタイミングでいい記事を見つけたので、今回はこの構成で行くことにしました。
サーバーサイドKotlin (Spring Boot / Doma 2) 入門
KotlinでDomaが動くのかイマイチわかりませんでしたがだめそうならJavaで書けばいいかと思って今回もDomaを使うことにしました。
思ったり感じたりしたこと
Lombokを使わなかった
まずLombokを使いませんでした。
特に@Data
アノテーションが便利だったのですが、今回はKotlinのData Classを使いました。
あと最近は@autowired
アノテーションを使わずにコンストラクタインジェクションを使っていたのもあってLombokの@RequiredArgsConstructor
アノテーションも必須でしたが、プライマリコンストラクタを使ったので、このアノテーションも必要なくなりました。
ただ、唯一@Slf4j
はうまく代用できなかったので今も思案中・・・。
KotlinでSpringBootは問題ないの?
自分で使っている感じでは問題ありませんでした。
今回のアプリケーションの規模がそれほど大きくないというのが理由ですが、それでもJavaのときにSpringを使っているときと何も変わらない感じで使えました。
KotlinでDoma2は問題ないの?
これも先のQiitaの記事を参考にしたのでDoma2のAPT関係はすべてJavaで書いています。
具体的に言うと、@Entityと@Daoアノテーションをつけるクラス(インタフェース)です。
KotlinはJavaが混在しても問題ない(場合が多いと思う)ので特に問題にはなりませんでした。
これはKotlinは関係ないのですがDomaのIDEサポートはIntelliJよりEclipseのほうがリッチなので少し開発効率には影響があったかもしれません。
ですがbuild時に問題がわかるので、それほど大きく影響はしなかったかもしれません。ただIDEでわかったほうが効率的。
便利だったこと
メソッド引数のデフォルト値
Javaではメソッドの引数のデフォルト値はもたせられないので自分はオーバーロードを使っていました。
public int getDays() {
return getDays(new Date());
}
public int getDays(Date date) {
}
Kotlinではメソッドのデフォルト値をもたせれるのでデフォルト値目的のオーバーロードは必要なくなりました。
def getDays(date: Date = Date()) {
}
Data Class
そのままデータ用のクラス。プロパティだけを持っており独自のメソッドの定義などはできない。
Javaで言うところのPOJO(語弊があればすいません。)みたいなクラス。
JavaではLombokで@Dataアノテーションをつけるようなクラス。
ただ、Data Classは継承ができないので注意が必要。
あとData Classを使うとインスタンスのコピーが簡単に行なえます。
Javaの時のようにBeanUtils系を使う必要がありません。
data class Post(
val title: String,
val tags: List,
val createdAt: LocalDateTime
)
@Test
fun testCopy() {
val post1 = Post("オリジナル", listOf("あ", "い", "う"), LocalDateTime.now())
println(post1)
val post2 = post1.copy(title = "コピー")
println(post2)
// アウトプット
// Post(title=オリジナル, tags=[あ, い, う], createdAt=2018-02-06T22:49:49.289)
// Post(title=コピー, tags=[あ, い, う], createdAt=2018-02-06T22:49:49.289)
}
ループ時のインデックス取得
Java1.5だったと思うのですが拡張for文が導入されてListなどはforeach
でループできるようになりました。
しかしforeachはループインデックスを取得できないので、カウンタもしくはfor
文を使うということが少なからずありました。
KotlinはListなどはwithIndexを使うことでループインデックスと値両方取得できます。
val names = listOf("太郎", "次郎", "三郎")
for ((index, name) in names.withIndex()) {
println("names[$index] = $name")
}
// output
// names[0] = 太郎
// names[1] = 次郎
// names[2] = 三郎
少しハマったところ
Mockito.anyのNull Pointer Exception
Mockitoも基本的には使えたのですがMockito.any()
がNullPointerException
を返してうまく動作しませんでした。
以下のページを参考にさせていただき対応。
Kotlin + Mockitoでany
Is it possible to use Mockito in Kotlin?
@JsonPropertyで設定したjsonのフィールド名が反映されない
Jacksonを使っていてdata classをシリアライズするときにdata classのプロパティをjson上では別の名前で出力するときに@JsonProperty
で設定してもうまく反映されませんでした。
@get:JsonPropertyを使って解決しました。
非常に気になるのですが原因までは追っていません。
以下を流し読みする限りだとJacksonはデフォルトだとprivateフィールドのアノテーションを読まないと書いてあるような気がしますが自分の訳が正確ではないかもしれません。
Usage of Jackson @JsonProperty annotation for kotlin data classes
まとめ
新しい言語で実装するときは生産性が一時的に落ちるということが多いと思うのですがKotlinはそこまで大きく下がらなかったように思います。
ただ、まだ慣れてはいないのでJavaコードをKotlinにコピー&ペーストした感じがしますが・・・。
もし次もKotlinを使える環境であれば積極的に使っていきたいと思います。