過去にjOOQのRecord型から値を取り出すときに、一旦nullableをArrow.ktのOption型に変換してから合成すると良いよ的な書き方をしたんだけど、
同じくArrow.ktのnullable DSLでもっとスッキリ書けるとわかったので改めて。
以下のようなコードがあり、 当該フィールドに値が入っていることは確定しているから !!
で強制的にnullableを無視している。これでも良いのだけど、コンパイラがめっちゃwarn出してきてただならぬ雰囲気になるんだよね。
val query = create.select().from(TODOS) val todos = query.fetch().map { record -> ToDo( id = record.get(TODOS.ID)!!, title = record.get(TODOS.TITLE)!!, body = record.get(TODOS.BODY)!!, created_at = record.get(TODOS.CREATED_AT)!!, modified_at. = record.get(TODOS.MODIFIED_AT)!! ) }
Arrow.ktのnullable DSLを利用するとこのようにできる。
※ 利用しているArrow.ktのバージョンは 1.2.0-RC
val todos = query.fetch().mapNotNull { record -> nullable { ToDo( id = record.get(TODOS.ID).bind(), title = record.get(TODOS.TITLE).bind(), body = record.get(TODOS.BODY).bind(), created_at = record.get(TODOS.CREATED_AT).bind(), modified_at = record.get(TODOS.MODIFIED_AT).bind() ) } }
万が一どこかの値がnullだった場合に、元のコードとしてはNullPointerExceptionがはっせいしてしまうところであるが、今回のコードだとmapしたあとの ToDo()
もnullになり、mapNotNull
で消えてくれるので無視することができる。
不用意にOption型に詰め直す必要がないのはとても楽である。
なお、 Raise<T>
を利用したパターンでこのように書くこともできる
val todos = query.fetch().mapNotNull { record -> nullable { ToDo( id = ensureNotNull(record.get(TODOS.ID)), title = ensureNotNull(record.get(TODOS.TITLE)), body = ensureNotNull(record.get(TODOS.BODY)), created_at = ensureNotNull(record.get(TODOS.CREATED_AT)), modified_at = ensureNotNull(record.get(TODOS.MODIFIED_AT)) ) } }
さらにさらに、このようなjOOQのRecord型の拡張を用意しておくとシュッとしたコードになる。
※ build.gradleでcontext receiverの設定を追加する必要あり
context(NullableRaise) fun <T> org.jooq.Record.getOrRaise(field: org.jooq.Field<T>) = ensureNotNull(get(field)) val todos = query.fetch().mapNotNull { record -> nullable { ToDo( id = record.getOrRaise(TODOS.ID), title = record.getOrRaise(TODOS.TITLE), body = record.getOrRaise(TODOS.BODY), created_at = record.getOrRaise(TODOS.CREATED_AT), modified_at = record.getOrRaise(TODOS.MODIFIED_AT) ) } }
参考: