Scalaでのエラー処理
1. 概要
このチュートリアルでは、カスタム例外と、Scalaで例外を処理する複数の方法(例外を完全に回避するいくつかの方法を含む)について説明します。
2. カスタム例外
Javaとまったく同じように、例外クラスを拡張することにより、Scalaでカスタム例外を作成します。
例外を作成しましょう:
case class DivideByZero() extends Exception
ここでは、カスタム例外DivideByZeroを作成します。 作成したら、他の例外と同じように、カスタム例外をスローまたはキャッチできます。
誰かがゼロで除算しようとしていることに気付いたときに、DivideByZero例外を使用してみましょう。
def divide(dividend: Int, divisor: Int): Int = {
if (divisor == 0) {
throw new DivideByZero
}
dividend / divisor
}
次に、Scalaで例外を処理する方法を見てみましょう。 Javaとは異なり、Scalaには複数の方法があり、ニーズに最適な方法を選択できます。
3. 試して/キャッチ/最後に
try / catch / finally キーワードグループは、例外を処理するための最もよく知られた方法です。
簡単に言うと、危険なコードを try ブロックでラップし、エラー処理をcatchブロックでラップします。 前に作成したdivideメソッドによってスローされたDivideByZero例外を処理しましょう。
def divideByZero(a: Int): Any = {
try {
divide(a, 0)
} catch {
case e: DivideByZero => null
}
}
ここでは、 DivideByZero 例外をキャッチして、nullを返すことで処理します。
オプションで、finallyブロックを追加することもできます。 finally ブロックのコードは、危険なコードが失敗したかどうかに関係なく、常に実行されます。
4. 試行/成功/失敗
例外を処理するためのよりクリーンな方法は、試行/成功/失敗を使用することです。 コードが成功した場合は、結果とともに Success オブジェクトを返し、失敗した場合は、Failureオブジェクトでエラーを渡します。
divideByZeroをSuccess/Failureで実装しましょう。
def divideWithTry(dividend: Int, divisor: Int): Try[Int] = Try(divide(dividend, divisor))
divideWithTry を呼び出すと、元のエラーを含むFailureオブジェクトが取得されます。
assert(divideWithTry(10, 0) == Failure(new DivideByZero))
divideWithTry の呼び出し元は、SuccessおよびFailure objects 、を使用して次のようにパターンマッチングを行うことができます。
val result = divideWithTry(10, 0) match {
case Success(i) => i
case Failure(DivideByZero()) => None
}
5. 例外のないエラー処理
これまで、例外を処理する方法を見てきましたが、他に何ができるか見てみましょう。
私たちのゼロ除算シナリオは、例外なく解決できます。これは、コーディング時によくあることです。 JVMにスタックトレースを生成させる代わりにオブジェクトを返すことには、パフォーマンス上の利点があります。
したがって、「キャッチしようとしている例外をスローしない」という格言に続いて、ユースケースに対処するための例外のない方法をいくつか見てみましょう。
5.1. オプション/一部/なし
divideByZeroメソッドのtry/ catch / finally 処理の欠点の1つは、 nullを返すことです。これにより、クライアントが結果を処理するのが面倒になります。
null を返す代わりに、Optionを返すことができます。
def divideWithOption(dividend: Int, divisor: Int): Option[Int] = {
if (divisor == 0) {
None
} else {
Some(dividend / divisor)
}
}
Option を使用すると、いくつかの点が変わります。 まず、関数の戻り型がIntではなくOption[Int]になっていることに注意してください。 通常の状況では、それは私たちが返すことを意味します
を呼び出すコード splitWithOption を使用してパターンマッチングできますいくつかとなし 、私たちがしたように成功と失敗ついさっき
オプションには1つの欠点があります。それは、元のエラーを飲み込むことです。発信者に正確に何が起こったかを知らせる方法はありません。
エラーを呼び出し元に返したい場合でも、試行/成功/失敗を使用するか、最終的なアプローチを確認する必要があります。
5.2. どちらか/左/右
もう1つの例外のない代替手段は、どちらかです。
どちらかは、LeftとRightで表される2つの相互に排他的な可能な値を表します。
エラーを処理するときの規則では、エラーには Left を使用し、結果にはRightを使用します。 Option / Some / None と比較すると、RightはSomeに似ており、LeftはNoneに似ています。 。
Either を使用して、ゼロ除算メソッドを書き直してみましょう。
def divideWithEither(dividend: Int, divisor: Int): Either[String, Int] = {
if (divisor == 0) {
Left("Can't divide by zero")
} else {
Right(dividend / divisor)
}
}
ここでは、値を Right として返し、エラーの場合は、元のエラーでLeftを返します。 発信者は、RightおよびLeftに対してパターンマッチングを行うことができます。
6. 結論
この記事では、Scalaでカスタム例外を作成する方法について説明しました。
次に、エラーを処理する4つの方法について説明しました。 従来のtry/ catch / finally から始めて、Scalaのオブジェクト指向アプローチを続けました。 最後に、例外をスローせずにエラーに対処する方法を示しました。
いつものように、すべてのコード例はGitHubのにあります。