こういうクラスがあり、newする時にエラー型を明示して Either<IllegalArgumentException, Email>
が返却されるようにしたいとする。
class Email(val address: String) { init { require(address.contains("@")) { "Invalid email address" } } }
とすると、このように書くことが多いのだが、呼び出しごとに Email.of("foo@bar.com")
みたいに書く必要があるのがやや面倒だと感じていた。
class Email private constructor(val address: String) { companion object { fun of(address: String): Either<IllegalArgumentException, Email> = if (address.contains("@")) { Email(address).right() } else { (IllegalArgumentException("Invalid email address")).left() } } }
of
をこのようにすると引き続き Email("foo@bar.com")
でEmailのインスタンスが作成できるようになる。
また、constructorと違ってcontext receiverやアノテーションもつけられるという点も良い。
operator fun invoke(address: String)): Either<IllegalArgumentException, Email>
ただinvokeに関して、IDE上でジャンプする時に直接Invokeには飛んでくれなくて、invokeが実装されているクラス今回だとcompanion objectに飛んでしまう。これは コンパイラがEmail()をコンパイル時にEmail.companion.invoke()に変換する
と振る舞うことをイメージできないとコードの追いづらさが上がるところがあり良し悪しあるなとは感じる。そのあたりを理解して使ったほうが良いかもしれない。
個人レベルでは良いが現場では使いづらいテクニック。
参考