🍀

Kotlinの標準APIを知る【Result編】

2021/12/10に公開

Kotlinの標準API Result

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/

良くCoroutinesと一緒に使う記事を見かけます。
が、今回は普通にSever Side Kotlinの中でどんな風に使えそうかを検討してみました。

標準APIのResultはどのようなものか?

処理の成功と失敗(例外)の二値を表現する用途で用いることができます。

public value class Result<out T> @PublishedApi internal constructor(
    @PublishedApi
    internal val value: Any?
)

上記のようなクラス定義になっており
Result.success(value:T)で処理に成功した場合に処理で得られた値をvalueに保持
Result.failure(exception: Throwable)で処理に失敗した場合に送出された例外インスタンスを内包したResult.Failureという内部クラスをvalueに保持
という形で処理の成功状態 or 失敗状態を表現させます

どう活用していくか

オーソドックスな使い方

runCatchingという関数に例外を送出する可能性のある処理を渡すことで処理に成功した場合はsucessで、失敗した場合はfailureでResultを返してくれます。

val result: Result<Type> = runCatching { 例外が送出される可能性のある処理 }

result
	.onSuccess { println("success") }
        .onFailure { println("error") }

標準APIのonSuccess,onFailure関数で成功だった場合、失敗だった場合の処理を簡潔に書けます(※try-catchやif文による結果の判定等、手続き的な記述を取らなくて済むようになります。)

Exceptionのラッピング

フレームワークや呼び出し先の例外を捕捉して、情報を追加や変換して送出し直したいみたいなケースがあるかと思います。

try {
	// 抽象的すぎる例外の送出
} catch (抽象的すぎる例外 e) {
	// throw わかりやすく具体化した例外(e)
}

普通に書くと上記みたいな感じになると思います。
これをResultで書きなおしてみると

val result: Result<Type> = runCatching { // 抽象的すぎる例外の送出 }
result.getOrElse { e -> throw わかりやすく具体化した例外(e) }

のようにネストなしで表現できます。

メソッドチェーン(成功したら次の処理をする)

通常のメソッドチェーンはbuilder的なもの等、処理が途中で失敗した場合のことを考慮しないケースで使われることが大半だと思います。ですが、稀に連続する処理で前の処理結果を受け渡しながら成功したら次の処理をするというケースにも出くわします。
このようなケースで愚直にやると

try {
	 val firstResult = 例外を送出する可能性のある処理_1
	 val secondResult = 例外を送出する可能性のある処理_2(firstResult)
	 return val thirdResult = 例外を送出する可能性のある処理_3(secondResult)
} catch (例外 e) {
	return エラー時のデフォルト戻り値
}

やや手続き的でちょっと嫌な感じがしてきます。そこでmapCatchingという関数を使った実装で書いてみます。

val result = runCatching { 例外を送出する可能性のある処理_1 }
        .mapCatching { 例外を送出する可能性のある処理_2(it) }
        .mapCatching { 例外を送出する可能性のある処理_3(it) }
        .getOrDefault(エラー時のデフォルト戻り値)

こんな感じで例外を送出する可能性のある処理をmapCatchingでラップすることで処理の結果をResultとしてチェーンしていくことができます。

まとめ

これまで標準的なJavaAPIでは手続き的な記載で冗長になってしまうような部分をResult型での結果処理という設計に切り替えると、宣言的にストレートに書ける可能性があることがわかりました。
みなさんの活用事例も是非コメント等で教えていただけるととても嬉しいです。

ステップアップ

標準APIの現在の実装は例外によるエラー表現しかできませんが、例外を用いないResultの実装を提供してくれているライブラリもあります。
https://github.com/michaelbull/kotlin-result

Resultで扱いたいからなんでも処理の失敗を例外で表現するというのは誤った設計に陥ってしまうので、こちらも試してみようと思います。(また、どこかで記事にします。)

Discussion