Example #10: Null checks with “Option-Some-None”
Nulls in Scala is a code smell, and a better way to handle null values is with an “Option“, which has 2 sub classes “Some” that has a value and a “None” that does not have a value. Instead of checking for “myVal != null”, you can use “pattern matching” to deal with null values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
object HelloNull extends App { def sayHi(input: Option[String]) : String = { val hello = "Hello " input match { case Some(input) => hello + input case _ => hello + "Null" } } val result1 = sayHi(Option("Peter")) println(result1) //Hello Peter val result2 = sayHi(Option(null)) println(result2) // Hello Null } |
You can alse use methods like “isDefined” and “isEmpty“, but pattern matching shown above is cleaner.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
object HelloNull extends App { def sayHi(input: Option[String]): String = { val hello = "Hello " if (input.isDefined) { hello + input.get } else { hello + "Null" } } val result1 = sayHi(Option("Peter")) println(result1) //Hello Peter val result2 = sayHi(Option(null)) println(result2) // Hello Null } |
“getOrElse” is a handy method that takes a “default” value when the value is null.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
object HelloNull extends App { def sayHi(input: Option[String]): String = { val hello = "Hello " hello + input.getOrElse("Null") } val result1 = sayHi(Option("Peter")) println(result1) //Hello Peter val result2 = sayHi(Option(null)) println(result2) // Hello Null } |
Example #11: Option class supports map, flatMap, and filter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
object HelloNull extends App { def sayHi(input: Option[String]): String = { val hello = "Hello " val inputOpt = input.map{ x => hello + x } inputOpt.getOrElse(hello + "Null") } val result1 = sayHi(Option("Peter")) println(result1) //Hello Peter val result2 = sayHi(Option(null)) println(result2) // Hello Null } |
Example #12: Walking an object tree to check for null at each stage with “for comprehensions”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
object ObjectGraphNullCheck extends App { def getDepartmentName(student: Student) : String = { val deptName = for { s <- Option(student) d <- Option(s.department) } yield { "My department is " + d.name } deptName.getOrElse("I have no department") } case class Student(name: String, department: Department) case class Department(name: String) println(getDepartmentName(new Student(null, null))) // I have no department println(getDepartmentName(new Student("John", new Department("Science")))) // My department is Science } |
The “for-comprehension” is a syntactic sugar for a “flatMap” followed by a “map” as shown below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
object ObjectGraphNullCheck extends App { def getDepartmentName(student: Student): String = { val deptName = Option(student).flatMap { x => Option(x.department).map { y => "My department is " + y.name } } deptName.getOrElse("I have no department") } case class Student(name: String, department: Department) case class Department(name: String) println(getDepartmentName(new Student(null, null))) // I have no department println(getDepartmentName(new Student("John", new Department("Science")))) // My department is Science println(getDepartmentName(new Student("John", new Department(null)))) // My department is null } |
The “Option” class in Scala is “Monadic“, which means a way to compose data. Option supports composition via flatMap, and that’s what required to be a Monad.
You can create your own “Monadic” class as “MyMonad” shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
object MyMonad extends App { def sum(num1: Integer, num2: Integer): MyMonad[Integer] = { for { a <- new MyMonad(num1) b <- new MyMonad(num2) } yield a + b } println(sum(3, 2)) } class MyMonad[A](value: A) { def map[B](f: A => B) = new MyMonad(f(value)) def flatMap[B](f: A => MyMonad[B]) = f(value) override def toString = value.toString } |
if you want to support “filter” function, then need to implement a “withFilter” function, and if looping is required a “forEach” function. We will look at “Functors, Applicative Functors, and Monads in a separate post.