Saturday, January 30, 2016

Optional get Method on a Java Map

Lately I've been doing both Java 8 (for work) and Scala (for fun and personal development). And although I like Java 8's functional features, they're naturally lacking compared to Scala.

One thing specifically that I wanted this week was a java.util.Optional-returning get() method on a Map. (I was dealing with nested maps at the time.) This method is something that clearly ought to exist but doesn't, probably because it would be too hard to engineer into the Map interface in any kind of backward-compatible way.

So I started a playground in my Github blog code repository for Functional Java and took a crack at building something to address this. What I came up with was a Map wrapper classed that I called "FuncMap". Its banner (and currently, only) feature is a new method called "getOption" that takes a key and returns an Optional-wrapped value:

  public Optional getOption(K key) {
    return Optional.ofNullable(get(key));

  }

That's all it takes to enable a Nullable-free programming style on the Map interface. The following three test cases demonstrate how this works with flatMap.
  @Test
  public void testNestedMapsFirstGetIsNull() {
    FuncMap<String, FuncMap<String, String>> nestedMap =
        new FuncMap<>(new HashMap<String, FuncMap<String, String>>());
    
    String result = nestedMap.getOption("user")
        .flatMap(x -> x.getOption("scott"))
        .orElse("UNKNOWN");
    assertEquals("UNKNOWN", result);
  }

  @Test
  public void testNestedMapsSecondGetIsNull() {
    FuncMap<String, FuncMap<String, String>> nestedMap =
        new FuncMap<>(new HashMap<String, FuncMap<String, String>>());
    nestedMap.put("user", new FuncMap<>(new HashMap<>()));
    
    String result = nestedMap.getOption("user")
        .flatMap(x -> x.getOption("scott"))
        .orElse("UNKNOWN");
    assertEquals("UNKNOWN", result);
  }

  @Test
  public void testNestedMapsWholeExpressionNotNull() {
    FuncMap<String, FuncMap<String, String>> nestedMap =
        new FuncMap<>(new HashMap<String, FuncMap<String, String>>());
    nestedMap.put("user", new FuncMap<>(new HashMap<>()));
    nestedMap.get("user").put("scott", "admin");
    
    String result = nestedMap.getOption("user")
        .flatMap(x -> x.getOption("scott"))
        .orElse("UNKNOWN");
    assertEquals("admin", result);
  }
Yeah, still a little clunky compared to Scala. But it's progress, right?

No comments: