Kotlin

Kotlinでサーバーサイドアプリを実装した感想

投稿日:

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()やeq()を使いたい
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を使える環境であれば積極的に使っていきたいと思います。

-Kotlin

執筆者:


comment

メールアドレスが公開されることはありません。

関連記事

KotlinのコードをktlintでチェックしてDangerでGitHubのPRへ通知する

最近、仕事で初めてKotlinでサーバーサイドアプリケーションを書く機会がありました。 少しでも慣れたほうがいいかなと思って何かないかなと思っていたらちょうどZabbix Senderを使っていたので …