Scala Saturday – The Option Type, Part 3

One more word on the Option type. While Option allows you a more type-safe alternative to nulls, it does not itself handle nulls. In other words, this …

val s = Some(null)

… gets you a Some containing a null. That doesn’t help so much if our aim is to get away from null, not kick it down the road.

The Java Way

In version 8, Java introduced its own Optional type, no doubt inspired by the Option and Maybe types available in the functional languages. It does handle nulls. Take the following classes as examples:

public class User {
  private String username;
  private String name;
  
  public User(String username) { this(username, null); }
  public User(String username, String name) {
    this.username = username;
    this.name = name;
  }
  
  public String getUsername() { return username; }
  public String getName() { return name; }
}

public class Session {
  private User user;
  
  public Session() { this(null); }
  public Session(User u) { this.user = u; }
  
  public User getUser() { return user; }
}

public class Application {
  private Session session;

  public Application() { this(null); }
  public Application(Session s) { this.session = s; }
  
  public Session getSession() { return session; }
}

Because Optional handles null in its map operation, this works:

Application app = new Application(new Session());
String name = Optional.of(app)
                .map(Application::getSession)
                .map(Session::getUser)
                .map(User::getName)
                .orElse("n/a");
// name = "n/a"

In our example, the Session has a null User property. But that’s OK, because the map operation on line 4 (highlighted above) takes that null and converts it to a None, or more accurately, an Empty per Java parlance.

Back in Scala Land

We cannot do quite the same thing in Scala. Now we, as enlightened Scala developers, would never code up something that uses nulls all over the place like the Java classes above. But the Java world is more comfortable with nulls, so let’s say that you are working with a library that contains those Java classes. You pull the JAR into your Scala project and write something like this:

val app = new Application(new Session())
val name = Option(app)
             .map(_.getSession)
             .map(_.getUser)
             .map(_.getName)
             .getOrElse("n/a")
// NullPointerException!!!

But that breaks down pretty quickly. Line 4 (highlighted above) yields an exception because _.getUser is null. Option.map in Scala does not convert a null to a None. So then, what do we do?

Well, you can instantiate an Option by applying the Option object. Option() does take an object reference and return a None if it is a null, or a Some if it is not. Also, remember that Option.flatMap takes an operation that returns an Option and flattens it so that it does not return a nested Option, but just an Option. Therefore, you can change your code to this:

let app = Application(Session())
val name = Option(app)
             .flatMap(a => Option(a.getSession))
             .flatMap(s => Option(s.getUser))
             .flatMap(u => Option(u.getName))
             .getOrElse("n/a")
// name : String = "n/a"

It’s a bit more headache than you’d like, but that’s what you get for using flaky Java code, right?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.