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:
parseNumber
is a function that attempts to transform aString
into anInt
.- if the
String
isn’t valid, the function returnsNone
. - the
try
block is where we put the code that can throw anException
. - The
catch
block specifies which exceptions we want to handle. - if an
Exception
is thrown but not caught by thecatch
block, 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!