Meet Google Guava's Optional class

Google Guava is a great library that should be part of every Java developer’s arsenal. As its user guide states, it includes classes for “collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, and so forth.” I often peruse Google Guava’s Javadoc. There’s just so much good stuff in there! I can learn about API design, discover ways to improve my software, and seeing such beautiful code just makes me feel good. (Yeah, I know. That’s about as cool as driving a moped on the sidewalk while wearing khaki flood pants with a ketchup stain from a street meat hot dog but whatever… it’s what I do!)

I stumbled upon another gem in Google Guava last week while improving my program that creates gamesheets for Ontario Hockey League games. When I attend an OHL game (and I often do… go Bulls!) I take gamesheets that include details about the players on both teams. The details include player name, player type (rookie or veteran), season statistics and streaks, biographical details, and sweater number. It’s the last detail that introduced me to Google Guava’s Optional class.

Optional is a simple immutable generic class that stores an optional non-null value. As its Javadoc states, Optional allows you to represent “a T that must be present” and a “a T that might be absent” as two distinct types in your program, which can aid clarity.” For hockey players, sweater numbers are sometimes not known. For example, when a player is traded to a new team, he can’t always use the sweater number of his old team because a more veteran player on his new team might already use it. Until he plays his first game for his new team, his sweater number is often unknown. So, what Java type should I use to represent a player’s sweater number?

Well, sweater numbers in hockey are positive integers ranging from 1-99 so I started out with the obvious - int. For example, the Player class contained the following method:

class Player
{
  int getSweaterNum()
  // Other methods elided for brevity.
}

This poses a problem for players with an unknown sweater number. What should getSweaterNum return for such a player? I initially took the lazy approach and used a magic number (0). However, I forgot to check for that magic number in my code that uses the Player class. It blindly called getSweaterNum and put 0 in the sweater number column of my gamesheets. How ugly! Rather than print a 0, the sweater number column should remain blank so that I can fill it in at the game when I see what number the player wears during the warmup.

There are a couple of approaches I could use to solve this problem:

  • Define a constant (say Player.UNKNOWN_SWEATER_NUM = 0) for an unknown sweater number.
  • Use an Integer instead of an int for sweater number and use null to indicate that the sweater number is unknown.
  • Create a SweaterNum class to encapsulate the concept of a sweater number.

I don’t really like any of these approaches. The first approach leads to ugly code at the calling site and doesn’t make it any more likely that the caller will remember to check for the magic value. The second approach violates the best practice of not returning null (see Clean Code: A Handbook of Agile Software Craftsmanship by Robert Martin - it’s a fantastic book!). The third approach seems a bit like overkill.

Instead of these approaches, I chose to use Optional<Integer> for the return value of Player.getSweaterNum:

class Player
{
  Optional<Integer> getSweaterNum()
  // Other methods elided for brevity.
}

This increases clarity; human readers know by the method’s return value that a player’s sweater number is optional. This makes it less likely that calling classes will forget to check whether a sweater number is known. Calling classes do the following instead:

private String getSweaterNumCellValue(final Optional<Integer> sweaterNum)
{
  if (sweaterNum.isPresent())
  {
    return Integer.toString(sweaterNum.get());
  }
  else
  {
    return EMPTY_TABLE_CELL;
  }
}

The method that determines a player’s sweater number does the following:

try
{
  final String sweaterNumString = // get from OHL website
  return Optional.of(Integer.parseInt(sweaterNumString));
}
catch (final NumberFormatException e)
{
  return Optional.absent();
}

This is by no means a perfect solution as it reveals some of Java’s clunkiness. For example, it would be nicer if we could use Optional<int> but, alas, Java does not allow that syntax. However, I think it’s a good compromise. What do you think?