Kotlin勉強メモ(その1)
次の案件でKotlinを使おうと思って勉強を始めました。
資料
とりあえず申込、契約管理の業務を想定したDDDのサンプルを作りながら勉強中(コードはいつか公開します)
今日のハイライト
Spring Initializrで簡単にテンプレートが作れた
Kotlinを選択すればSpring Bootプロジェクトのサンプルがダウンロードできる。
sealed class
詳細は上記リンクを見ていただくとして、sealed classを使って「申込がないときは申込できる(すでに申込済みのときは申込できない)」という業務ロジックをこんな感じで表現してみました。
/** * 申込エンティティ */ class OrderEntity(val order: Order) { /** * 申し込む * note : Eitherがないので例外をスローしている。できればエラーを表現した型で返却したい。 * * @param ordAt : 申込日 * @return 申込イベント * @throws RuntimeException 申込ではないとき */ fun order(ordAt: OrdAt): OrderEvent = when (order) { is Order.NotExist -> OrderEvent(ordAt) is Order.Progressing -> throw RuntimeException("already ordered") is Order.Completed -> throw RuntimeException("already completed") } } /** * 申込 */ sealed class Order { class NotExist : Order() class Progressing(val id: Id, val ordAt: OrdAt) : Order() class Completed(val id: Id, val completeAt: CompleteAt) : Order() } /** * 申込イベント */ data class OrderEvent(val ordAt: OrdAt) /** * 取消イベント */ data class CancelEvent(val id: Id, val cancelAt: CancelAt) /** * 完了イベント */ data class CompleteEvent(val id: Id, val completeAt: CompleteAt) data class Id(val value: Int) data class OrdAt(val value: LocalDateTime) data class CancelAt(val value: LocalDateTime) data class CompleteAt(val value: LocalDateTime)
when句の中でsealed classの実装クラスを網羅できていないとコンパイルエラーにしてくれるところがイケてると思いました。
例:上記のコードをこうすると
fun order(ordAt: OrdAt): OrderEvent = when (order) { is Order.NotExist -> OrderEvent(ordAt) is Order.Progressing -> throw RuntimeException("already ordered") // is Order.Completed -> throw RuntimeException("already completed") }
以下のようなコンパイルエラーになる
$ ./gradlew clean compileKotlin :clean :compileKotlin e: /Users/******/Develop/git/ddd-sample-kotlin/src/main/kotlin/work/doilux/dddsamplekotlin/ftth/order/Order.kt: (29, 43): 'when' expression must be exhaustive, add necessary 'is Completed' branch or 'else' branch instead :compileKotlin FAILED FAILURE: Build failed with an exception.
Nullable
Nullが入る可能性があるところは?をつけた型にしておくとNullチェックがない場合はコンパイルエラーになります。 以下のように使いました。
/** * 参照用のクラス。 型の後の?はNullableを表す。 */ data class OrderState( val id: Int, val status: String, val orderAt: LocalDateTime?, val cancelAt: LocalDateTime?, val completeAt: LocalDateTime? )
Either、Optionはない
Scalaと違ってEither、Optionはなさげ。とりあえず、郷に入れば郷に従えでOptionについてはNullableで頑張ってみます(peekとか使いたくなりそう)Eitherは、ドメイン表現については前述のsealed classを使った実装もありですが、エラー返却でほしい(ドメイン層で例外スローは絶対やめたい。。。)Kotlinで作るか、vavrを使うかは考えます。