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オブジェクトでエラーを渡します。

divideByZeroSuccess/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つの例外のない代替手段は、どちらかです。

どちらかは、LeftRightで表される2つの相互に排他的な可能な値を表します。

エラーを処理するときの規則では、エラーには Left を使用し、結果にはRightを使用します。 Option / Some / None と比較すると、RightSomeに似ており、LeftNoneに似ています。 。

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にあります。