Handling Exceptions using Try/Catch/Finally in Scala
Julien Truffaut
25 September 2023
Exception handling is a critical aspect of software development, and Scala provides powerful tools to manage exceptions effectively. In this guide, we’ll dive into the Try/Catch/Finally construct.
Try/Catch/Finally: An Overview
Try/Catch/Finally is a low level expression for handling exceptions in Scala. Let’s explore how it works using a simple example:
def parseNumber(line: String): Option[Int] =
try {
val number = line.toInt
Some(number)
} catch {
case e: NumberFormatException =>
None
}
parseNumber("1234") // Option[Int] = Some(1234)
parseNumber("-1") // Option[Int] = Some(-1)
parseNumber("hello") // Option[Int] = None Here’s a breakdown of the code:
parseNumberis a function that attempts to transform aStringinto anInt.- if the
Stringisn’t valid, the function returnsNone. - the
tryblock is where we put the code that can throw anException. - The
catchblock specifies which exceptions we want to handle. - if an
Exceptionis thrown but not caught by thecatchblock, it propagates up the call stack.
Catching Multiple Exceptions
The catch block behaves like a normal pattern match on a Throwable, the top level class of all errors and exceptions.
This enables us to handle different exceptions in distinct ways:
try {
// Risky code
} catch {
case e: NumberFormatException => // Handle NumberFormatException
case e: IOException => // Handle IOException
} Catching All Exceptions
It’s also possible to catch all exceptions by not specifying an exception type:
try {
// Risky code
} catch {
case e => // Handle any Throwable (equivalent to case e: Throwable)
} Fatal errors
While catching exceptions is essential, not all errors are recoverable. For example, an OutOfMemoryError is typically fatal and should not be handled. Scala provides a convenient function called NonFatal to detect if an exception is non-fatal:
import scala.util.control.NonFatal
try {
// Risky code
} catch {
case NonFatal(e) => // Handle non-fatal exceptions
} This approach ensures that only non-fatal exceptions are caught, leaving fatal errors unhandled.
Using Try for Exception Handling
Alternatively, we can use Try from the scala.util package when we want to catch all non-fatal errors.
import scala.util.Try
def parseNumber(line: String): Try[Int] =
Try(line.toInt)
parseNumber("1234") // Try[Int] = Success(1234)
parseNumber("-1") // Try[Int] = Success(-1)
parseNumber("hello") // Try[Int] = Failure(java.lang.NumberFormatException: For input string: "hello")
Try encapsulates the result of an operation, either as a Success or a Failure similar to Option or Either.
In summary, Scala offers various mechanisms for handling exceptions effectively. Whether you choose the traditional Try/Catch/Finally approach or leverage the Try class is essential for writing robust and reliable Scala code. In the next part, we’ll explore the finally block for running post-processing operations, enhancing your exception management skills. Stay tuned!