Julien Truffaut
25th 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 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 a String
into an Int
.String
isn't valid, the function returns None
.try
block is where we put the code that can throw an Exception
.catch
block specifies which exceptions we want to handle.Exception
is thrown but not caught by the catch
block, it propagates up the call stack.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
}
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)
}
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.
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!