There’s a series of events to which any Java developer can relate: You’ve spent several minutes hacking on a piece of code and you are ready to test it out. You fire up an instance of your application, click the button you just added to the interface and BAM! NullPointerException! I haven’t sorted out exactly what the limit is, but I am of the opinion that, for any Java code change of more than a certain number of lines, the laws of the universe state that there will be a NullPointerException on the first execution.
Perhaps you have heard of Scala as the solution to every problem in Java. You should know that this is an overstatement, but you should also know that Scala’s standard library includes a class which can, if used correctly, almost completely eliminate NullPointerExceptions from your application. That class is called Option.
Usually, NullPointerExceptions happen when a method returns null to indicate that a proper value could not be returned (e.g. in a HashMap lookup). The NullPointerException occurs when you are unaware that a call may return null (either because you never knew or you forgot), so you try to call a method on the returned value without checking it against null. Read Chapter 3.2. Scala List, Tuple, Option, and Map Classes to learn even more about Options in Beginning Scala.
Enter Option: Option exists as an explicit way to describe values that may or may not exist. An Option can either contain a (single) value or it can be empty. This makes it the perfect tool for methods for which there is no proper return value in some cases. As an example, let’s look at the get method for Scala’s HashMap class, which has a signature like:
def get(key: KeyType): Option[ValueType]
If the searched-for key does not map to a value, get returns an empty Option. If it does map to a value, an Option containing that value is returned. The great advantage is that if you forget that the result of a get may not exist and try to add 3 to it, you will get a compiler error, since you can’t add 3 to an Option.
Option has a number of useful methods, but only a few are relevant to this introductory discussion. First, Option has a pair of Boolean-returning methods, called isDefined and isEmpty, which always return opposite values (an Option is either defined or empty). As you might guess, a defined Option wraps a value, while an empty Option does not. Option also has a method called get. This method will return either the wrapped value (if it exists) or throw an exception (if the Option is empty). Finally, there is a much safer method, called getOrElse, which is best describe as a get with a default value. When calling this method, you pass it an argument. The method will either return the wrapped value (if there is one) or the passed-in value (if there isn’t).
Defined Options are created by calling Some on values. For example, Some(42) creates a defined Option wrapping the number 42. The singleton object None is the empty Option. Due to the flexibility of Scala’s type system, you can return None from any method with Option as its return type, and pass None whenever an Option is expected.
Here’s a complete example:
object OptionTest extends App {
def checkDefined(o: Option[Int]) {
if (o.isDefined) println("Defined")
else println("Not Defined")
}
def printWithDefault(o: Option[Int]) {
println(o.getOrElse(0))
}
checkDefined(None) // Prints "Not Defined"
checkDefined(Some(3)) // Prints "Defined"
val map = Map("A" -> 1, "B" -> 2) // HashMap mapping "A" to 1, "B" to 2
checkDefined(map.get("A")) // Prints "Defined"
printWithDefault(map.get("B")) // Prints 2
printWithDefault(map.get("C")) // Prints 0
}
If you use Option regularly, it is definitely worth your time to learn about its map and flatMap methods, but we’ve covered enough to get you started writing NullPointerException-free code.