This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 3.17, “How to declare a variable (var) before using it in try/catch/finally.”
Problem
You want to use an object in a Scala try
block, and need to access it in the finally
portion of the block, such as when you need to call a close
method on an object.
Solution
In general, declare your field as an Option
before the try/catch block, then create a Some
inside the try
clause. This is shown in the following example, where the fields in and out are declared before the try/catch block, and assigned inside the try
clause:
import java.io._ object CopyBytes extends App { var in = None: Option[FileInputStream] var out = None: Option[FileOutputStream] try { in = Some(new FileInputStream("/tmp/Test.class")) out = Some(new FileOutputStream("/tmp/Test.class.copy")) var c = 0 while ({c = in.get.read; c != −1}) { out.get.write(c) } } catch { case e: IOException => e.printStackTrace } finally { println("entered finally ...") if (in.isDefined) in.get.close if (out.isDefined) out.get.close } }
In this code, in
and out
are assigned to None
before the try
clause, and then reassigned to Some
values inside the try
clause if everything succeeds. Therefore, it’s safe to call in.get
and out.get
in the while
loop, because if an exception had occurred, flow control would have switched to the catch
clause, and then the finally
clause before leaving the method.
Normally I tell people that I wish the get
and isDefined
methods on Option
would be deprecated, but this is one of the few times where I think their use is acceptable, and they lead to more readable code.
Another approach you can employ inside the try
clause is to use the foreach
approach with a Some
:
try { in = Some(new FileInputStream("/tmp/Test.class")) out = Some(new FileOutputStream("/tmp/Test.class.copy")) in.foreach { inputStream => out.foreach { outputStream => var c = 0 while ({c = inputStream.read; c != −1}) { outputStream.write(c) } } } } // ...
This is still readable with two variables, and eliminates the get
method calls, but wouldn’t be practical with more variables.
Discussion
One key to this recipe is knowing the syntax for declaring Option
fields that aren’t initially populated:
var in = None: Option[FileInputStream] var out = None: Option[FileOutputStream]
I had a hard time remembering this until I came up with a little mnemonic, “Var x has No Option yeT,” where I capitalize the “T” there to stand for “type.” In my brain it looks like this:
var x has No Option[yeT]
From there it’s a simple matter to get to this:
var x = None: Option[Type]
When I first started working with Scala, the only way I could think to write this code was using null
values. The following code demonstrates the approach I used in an application that checks my email accounts. The store
and inbox
fields in this code are declared as null
fields that have the Store
and Folder
types (from the javax.mail package):
// (1) declare the null variables var store: Store = null var inbox: Folder = null try { // (2) use the variables/fields in the try block store = session.getStore("imaps") inbox = getFolder(store, "INBOX") // rest of the code here ... catch { case e: NoSuchProviderException => e.printStackTrace case me: MessagingException => me.printStackTrace } finally { // (3) call close() on the objects in the finally clause if (inbox != null) inbox.close if (store != null) store.close }
However, working in Scala gives you a chance to forget that null
values even exist, so this is not a recommended approach. See Recipe 20.5, “Eliminate null Values from Your Code”, for examples of how to rid your code of null
values.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
See Also
- The code shown in this recipe is a Scala version of this Oracle “Byte Streams” example