Catching Exceptions in Scala (Part 2)
Trying to use the try block to try out code might be a bad try. If you want to try code, well, try wrapping it in a Try type instead.
Join the DZone community and get the full member experience.
Join For FreeIt’s time to continue our previous blog on how to make our code more robust, concise, and more functionally defined at the same time. Here, we are moving more toward Scala and leaving traditional Java behind. We will see new types and their usage along with the benefits we get from them.
This blog will show you the best way, in my perspective, to handle the exceptions gracefully in Scala (the beloved coding language for everyone here at Knoldus). Our methods would only be responsible for either returning the results or the exceptions, i.e. we will not write any try-catch blocks to handle the exceptions within different methods.
You might be thinking by now that we will be talking about the Either type in Scala next to achieve the composition of methods. Although we can do that, it's quite different than the desired functionality. We won't use it here due for the following two reasons:
- The Either type works when you are explicitly defining what will be kept within Left (generally depicting failure) or Right (generally depicting success), i.e. your code would determine what kind of exception message will be sent in the Left instance and what return value you want in the Right instance.
- In order to define the correct message for the exception that might occur, you might want another try-catch block for different exception cases.
Instead of using try-catch blocks all over the code, we should work with scala.util.Try to handle all the exceptions in the code. It is easier to implement, compose, and transform. We will see the usage of Try in this block, but we are only looking at the ways of handling exceptions in methods that are sequential in nature. If we need to work on concurrent methods that may run on different threads simultaneously, Futures in Scala provide direct exception handling support via recover.
Instead of wrapping your code in a try-catch block, just wrap it within a Try block.
val result: Try[Int] = Try(riskyCodeInvoked("Exception Expected in certain cases"))
result match {
case Success(res) => info("Operation Was successful")
case Failure(ex: ArithmeticException) => error("ArithmeticException occurred", ex)
case Failure(ex) => error("Some Exception occurred", ex)
}
Now, to explore the advantages of using Try instead of a try-catch block further, consider the following example:
def riskyCodeInvoked(input: String): Int = ???
def anotherRiskyMethod(firstOutput: Int): String = ???
def yetAnotherRiskyMethod(secondOutput: String): Try[String] = ???
val result: Try[String] = Try(riskyCodeInvoked("Exception Expected in certain cases"))
.map(anotherRiskyMethod(_))
.flatMap(yetAnotherRiskyMethod(_))
result match {
case Success(res) => info("Operation Was successful")
case Failure(ex: ArithmeticException) => error("ArithmeticException occurred", ex)
case Failure(ex) => error("Some Exception occurred", ex)
}
In the above code, we are composing different methods together using transformations on the Try type. It removes all the boilerplate try-catch blocks and also allows you to handle all exceptions at one place. If the method riskyCodeInvoked is causing an exception, control will not go to anotherRiskyMethod and so forth.
If you are more comfortable with Option type, you can directly use Try like this:
val result: Try[String] = ???
val resultOpt: Option[String] = result.toOption
resultOpt.map{case res: String => info("Operation Was successful")}
There are other methods that are also defined on the Try type, like recover, getOrElse, orElse, isSuccess, isFailure, Filter, etc. Once you get used to it, you might not want to use try-catch blocks ever again! So try it out in your code and enjoy the simplicity of Scala.
Published at DZone with permission of Bharat Singh, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Merge GraphQL Schemas Using Apollo Server and Koa
-
Implementing a Serverless DevOps Pipeline With AWS Lambda and CodePipeline
-
Operator Overloading in Java
-
Mastering Time Series Analysis: Techniques, Models, and Strategies
Comments