Skip to content
Advertisement

Handle java.lang.NullPointerException in scala when accessing nested fields

I have a use case where

    name = Person.getFirstSister().getname()

Person.getSister().getname() gives java.lang.NullPointerException since Person.getSister() is java null

What is the right way to do this in scala. I am able to achieve with below code. But looking for more scala way of doing it.

  private def getFirstSisterName(person: Person): String = {
    val sister = person.getFirstSister()
    if (sister == null) ""
    else sister.getName()
  }

Edit: I changed the code in the following way

private def getFirstSisterName(person: Person): Option[String] = 
Option(person).flatMap(l => Option(l.getFirstSister)).map(_.getName)

Advertisement

Answer

Checking for null is the Java way to go. Scala offers a much more streamlined set of operations to avoid doing null checks manually.

Naturally, getSister should result in an Option[Sister], the argument being that not every person has a sister.

Going on the same idea, trying to mimic the real word, I would ask: but what if a person has multiple sisters? How do you distinguish between them? This implies your design is not very general because it implies every person has exactly one sister.

So the idiomatic way is to have the method return a List[Sisters]. Then map their names from the list. If the list is empty, Nil, the empty list will be the result, so we won’t have any NPE:

  case class Sister(name: String)
  case class Person(sisters: List[Sister]) {
    def sistersNames: String = sisters.map(_.name).mkString(" ")
  }

  val p = Person(List(Sister("Sandra"), Sister("Joanna")))
  val p2 = Person(List.empty)

  println(p.sistersNames)    // Sandra Joanna
  println(p2.sistersNames)   // empty String

EDIT:

If you can’t change Person or Sister, the alternative is to make your wrapper method treat the null case. You can do this in 2 ways, either use pattern matching, or wrap the result in an Option. Note: Option(x) checks if x is null and if it is, it converts it to a None. That is why applying fold works.

  private def getFirstSisterName(person: Person): String =
    person.getFirstSister match {
      case s: Sister => s.name
      case _         => s"${person.toString} does not have a sister"
    }

  private def getFirstSisterName2(person: Person): String = {
    val sis = Option(person.getFirstSister)
    sis.fold(s"${person.toString} does not have a sister")(_.name)
  }

  val p = new Person(new Sister("Sandra"))
  val p2 = new Person(null)

  println(getFirstSisterName(p))     // Sandra
  println(getFirstSisterName2(p))    // Sandra
  println(getFirstSisterName(p2))    // person827966648 does not have a sister
  println(getFirstSisterName2(p2))   // person827966648 does not have a sister
Advertisement