17.8. Zamiana wyjątków na wartości
Wyjątki można przechwytywać i zamieniać na obiekty typu Option lub Either. Obiekt Exception z pakietu scala.util.control umożliwia przechwytywanie wyjątków i zamianę ich na wartości. Metody div1 i div2 z pliku ToValues1.scala ilustrują dwa spośród możliwych sposobów jego użycia.
Plik ToValues1.scala: import scala.util.control.Exception._ def div1(a: Int, b: Int) = catching(classOf[ArithmeticException]) opt a/b def div2(a: Int, b: Int) = catching(classOf[ArithmeticException]) either a/b
Obie metody wykonują dzielenie wartości parametrów a i b przechwytując mogący się pojawić wyjątek ArithmeticException. Metoda div1 zwraca wartość typu Option[Int], a metoda div2 wartość typu Either[Throwable,Int]. W przypadku poprawnego wykonania dzielenia metoda div1 zwraca wynik opakowany w instancję klasy Some, a metoda div2 wynik opakowany w instancję klasy Right. W przypadku wystąpienia wyjątku ArithmeticException pierwsza z metod zwraca obiekt None, a druga zwraca obiekt wyjątku opakowany w instancję klasy Left.
scala> :load ToValues1.scala Loading ToValues1.scala... import scala.util.control.Exception._ div1: (a: Int, b: Int)Option[Int] div2: (a: Int, b: Int)scala.util.Either[Throwable,Int] scala> div1(1,0) res0: Option[Int] = None scala> div1(4,2) res1: Option[Int] = Some(2) scala> div2(1,0) res2: scala.util.Either[Throwable,Int] = Left(java.lang.ArithmeticException: / by zero) scala> div2(4,2) res3: scala.util.Either[Throwable,Int] = Right(2)
Metoda safe z pliku ToValues1.scala pokazuje inny przykład, w którym przechwytywane są dwa rodzaje wyjątków: ArithmeticException i NumberFormatException, a metoda zwraca wartość typu Either[Throwable,Int].
Plik ToValues2.scala: import scala.util.control.Exception._ def safe(c: => Int) = (catching(classOf[ArithmeticException]) or catching(classOf[NumberFormatException])). andFinally{ println("finally safe") }.either(c)
Dodatkowo, niezależnie od przebiegu obliczeń wartości c — zarówno w przypadku wystąpienia wyjątku jak i w przypadku jego braku — wykonywany jest kod przekazany metodzie andFinally.
scala> :load ToValues2.scala
Loading ToValues2.scala...
import scala.util.control.Exception._
safe: (c: => Int)scala.util.Either[Throwable,Int]
scala> safe{4/2}
finally safe
res4: scala.util.Either[Throwable,Int] = Right(2)
scala> safe{1/0}
finally safe
res5: scala.util.Either[Throwable,Int] = Left(java.lang.ArithmeticException: / by zero)
scala> safe{"a".toInt}
finally safe
res6: scala.util.Either[Throwable,Int] = Left(java.lang.NumberFormatException: For input string: "a")
Obiekt Try z pakietu scala.util również pozwala na zamianę wyjątków na wartości. Wyrażenie przekazane metodzie apply tego obiektu jest ewaluowane i jeśli w wyniku tej ewaluacji zostanie wygenerowany wyjątek, to ten wyjątek zostanie przez metodę apply zwrócony, opakowany w instancję klasy Failure. W przeciwnym wypadku metoda apply zwróci wynik ewaluacji wyrażenia opakowany w instancję klasy Success. Failure oraz Success są podklasami klasy Try.
Plik ToValues3.scala: import scala.util.Try def div3(a: Int, b: Int) = Try( a/b )
Plik ToValues3.scala zawiera przykład użycia obiektu Try.
scala> :load ToValues3.scala Loading ToValues3.scala... import scala.util.Try div3: (a: Int, b: Int)scala.util.Try[Int] scala> div3(4,2) res7: scala.util.Try[Int] = Success(2) scala> div3(1,0) res8: scala.util.Try[Int] = Failure(java.lang.ArithmeticException: / by zero)
Plik ToValues1.scala:
import scala.util.control.Exception._
def div1(a: Int, b: Int) = catching(classOf[ArithmeticException]) opt a/b
def div2(a: Int, b: Int) = catching(classOf[ArithmeticException]) either a/b
