Published 2014-01-11.
Last modified 2019-04-22.
Time to read: 3 minutes.
Scala’s Option provides a way to eliminate null pointer exceptions.
The source code for this lecture is in
courseNotes/.
Java methods often return null when there is no value to be returned.
This is a leading source of runtime errors.
Scala offers the Option container, which allows you to handle the two cases:
Some value is returned, or the value is None.
The Option construct has been cleverly implemented, and it is actually a collection of zero or one items.
That means you can iterate over its contents to work with any value that might be within.
Because Option is a container, when you declare a variable to be of type Option
you must also declare the type that may be contained by the Option.
For example, here is how we could declare a variable called maybeAnswer, which might contain an Int.
The value stored in the variable is wrapped in a subclass of Option called Some,
which contains the value we are interested in.
scala> val maybeAnswer: Option[Int] = Some(42) maybeAnswer: Option[Int] = Some(42)
Scala 2.13 introduced the
StringOps.toIntOption
method, which attempts to parse an Int from a String.
The toIntOption method either returns Some[Int] or None if the parse was unsuccessful.
scala> val maybeAnswer: Option[Int] = "42".toIntOption res0: Option[Int] = Some(42)
We can obtain the value of an Option that has Some value with get,
although that is not what you should usually do.
More on that later.
scala> maybeAnswer.get res1: Int = 42
We’ll look at the other StringOps.toXXXOption methods in a moment.
Similarly, we can declare a variable that might contain a String containing the name of our favorite type of chocolate, or
None if we don’t like chocolate.
scala> val maybeFavorite: Option[String] = None maybeFavorite: Option[String] = None
scala> maybeFavorite.getOrElse("Bleah!") res1: String = Bleah!
However, if we attempt to obtain the value of our favorite chocolate, we will get an error if we use get.
Instead, we use getOrElse, which provides a default value if the Option contains None:
scala> maybeFavorite.get java.util.NoSuchElementException: None.get
scala> maybeFavorite.getOrElse("Nope") res6: String = Nope
We can also perform an action if an Option contains Some value:
scala> maybeAnswer.foreach { x => println(3 * x) } // prints the value of 42 times 3 126
scala> maybeFavorite.foreach { println } // does not print anything
If an Option has Some value, its isDefined method will return true;
otherwise, if the Option has None value isEmpty will return true.
scala> val object1 = Some("Hi there") object1: Some[java.lang.String] = Some(Hi there)
scala> object1.isDefined res11: Boolean = true
scala> object1.isEmpty res12: Boolean = false
scala> val object2 = None object2: None.type = None
scala> object2.isDefined res13: Boolean = false
scala> object2.isEmpty res14: Boolean = true
Scala 2.13’s Other StringOps.toXXXOption Methods
Scala 2.13 introduced better StringOps parsing methods in addition to toIntOption,
which we have already seen.
scala> "true".toBooleanOption res3: Option[Boolean] = Some(true)
scala> "false".toBooleanOption res4: Option[Boolean] = Some(false)
scala> "true".toBooleanOption.foreach( if (_) println("Yes"); else println("No") ) Yes
scala> "asdf".toBooleanOption.foreach( if (_) println("Yes"); else println("No") ) No output because foreach does not iterate over None
scala> "asdf".toBooleanOption.map( if (_) "Yes"; else "No" ) res5: Option[String] = None
scala> "123".toBooleanOption res6: Option[Boolean] = None
scala> "1".toDoubleOption res7: Option[Double] = Some(1.0)
scala> "asdf".toDoubleOption res8: Option[Double] = None
Wrapping Nulls with Option
Here is where Option gets interesting.
First imagine that you want to retrieve the value of an environment variable.
scala> Option(System.getProperty("os.name")) res15: Option[java.lang.String] = Some(Windows 7)
Because we are sure that the result is wrapped in a Some instance, we call get to retrieve it
(we will discover soon that it usually is better to use map,
foreach or other iterators instead of calling get).
scala> Option(System.getProperty("os.name")).get res16: java.lang.String = Windows 7
If the environment variable is not set, Option will return None instead of null.
scala> Option(System.getProperty("not.present")) res17: Option[java.lang.String] = None
orElse
We can use orElse() to provide a default value if the Option’s value was None.
scala> Option(System.getProperty("not.present")).orElse(Some("default")) res3: Option[String] = Some(default)
scala> Option(System.getProperty("os.name")).orElse(Some("default")) res4: Option[String] = Some(Linux)
The above returns an Option if the system property has a value or not.
Now we can use get to retrieve whichever value comes back.
scala> Option(System.getProperty("not.present")).orElse(Some("default")).get res19: Option[java.lang.String] = default
getOrElse
We can use getOrElse to retrieve the value of the Option,
or the value of the getOrElse arguments if the
Option’s value was None.
scala> Option(System.getProperty("not.present")).getOrElse("default") res18: String = default
scala> Option(System.getProperty("os.name")).getOrElse("default") res6: String = Linux
Option is actually a collection of zero or one items.
This means that collection operations work on Option instances.
This leads to useful Scala idioms, such as provided by Option.fold.
We will discuss that in the next course.
It is also helpful to think of Option as a container.
Exercise
Write a Scala console app that checks the value of an environment variable.
If it is defined, print out its value, otherwise print out "undefined".
Solution
This solution can be found in
courseNotes/".
object TupleAnswer extends App {
def osProp(name: String): String = Option(System.getProperty(name)).getOrElse("undefined")
if (args.length>=1)
println(s"""Value of ’${args(0)}’ system property is ’${osProp(args(0))}’""")
}
To run it, type:
$ sbt "runMain solutions.TupleAnswer os.name"
$ sbt "runMain solutions.TupleAnswer user.home"
© Copyright 1994-2024 Michael Slinn. All rights reserved.
If you would like to request to use this copyright-protected work in any manner,
please send an email.
This website was made using Jekyll and Mike Slinn’s Jekyll Plugins.