Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

More on Java 8 Optional Functionality

DZone's Guide to

More on Java 8 Optional Functionality

Still relatively new to Java 8? Here's a newcomer's perspective of Optional to hopefully clear up a few confusing parts of its design and use.

· Java Zone ·
Free Resource

Get the Edge with a Professional Java IDE. 30-day free trial.

A recent article on a proposed "isEmpty" method addition to the Java 8 Optional API piqued my curiosity. I'm not using Java 8 on a day-to-day basis, so I don't know its features very well yet. Several years ago (probably not long after Java 8 was released), I wrote a program to play with lambda expressions. The Optional API was something I had yet to investigate, until today. In order to learn about Optional, I dusted off that old lambda program (Person.java and PersonTest.java in this GitHub repository) and added additional JUnit test cases to it.

The Basics of Optional

The first test cases I added to PersonTest were:

@org.junit.Test
public void testOptionalNotPresent() {
    Optional<String> email1 = person1.getEmailAddress();
    email1.ifPresent(value -> Assert.fail("Should not be here"));
    Assert.assertEquals("Optional.empty", email1.toString());
}

@org.junit.Test
public void testOptionalPresent() {
    person1.setEmailAddress(EMAIL_ADDR);
    Optional<String> email1 = person1.getEmailAddress();
    email1.ifPresent(value -> Assert.assertEquals(EMAIL_ADDR, value));
    Assert.assertEquals(String.format("Optional[%s]", EMAIL_ADDR), email1.toString());
}


These don't require much explanation, I think. I did scratch my head for a while trying to figure out what this Optional thing is doing, until I realized that I needed to use "Optional.ofNullable" instead of "Optional.of" in my implementation of Person class getEmailAddress method:

public Optional<String> getEmailAddress() {
    return Optional.ofNullable(emailAddress);
}


It's still not clear to me why anyone would want to create Optional objects which are NOT nullable. I mean that's the whole point, isn't it?

Empty Strings

I was also curious about the Optional API's use of the word "empty" and whether that applied only to null values, or if empty String objects qualified as "empty" as well. As it turns out, Optional lets you decide for yourself. I found an excellent answer to this question at StackOverflow. (See top answer "You could use a filter" in the referenced SO article)

So my next two cases were:

@org.junit.Test
public void testOptionalEmptyString_NotAllowed() {
    final String emailAddr = "";
    person1.setEmailAddress(emailAddr);
    Optional<String> email1 = person1.getEmailAddress();
    email1.ifPresent(value -> Assert.fail("Should not be here"));
    Assert.assertEquals("Optional.empty", email1.toString());
}

@org.junit.Test
public void testOptionalEmptyString_Allowed() {
    final String emailAddr = "";
    person1.setEmailAddress(emailAddr);
    person1.setAllowEmpty(true);
    Optional<String> email1 = person1.getEmailAddress();
    email1.ifPresent(value -> Assert.assertEquals(emailAddr, value));
    Assert.assertEquals(String.format("Optional[%s]", emailAddr), email1.toString());
}


The "Not Allowed" test case above tests the default behavior of the Person class, which is that empty email addresses are treated like null (not present). In the "Allowed" version of the test case, the Person object is mutated to allow empty strings (treated as present). In order to accomplish this my implementation of Person getEmailAddress method changed to:

public Optional<String> getEmailAddress() {
    return allowEmpty ? Optional.ofNullable(emailAddress) : Optional.ofNullable(emailAddress).filter(s -> !s.isEmpty());
}


As you can see, this is where I lifted the "Use a filter" answer from the SO article.

Functional Syntax

Finally, I came across another interesting DZone article about how the Optional API is based on monads. This was outside of my wheelhouse, so I drummed up another couple of test cases to explore that idea:

@org.junit.Test
public void testOptional_Monad_NotFound() {
    OptMap<String, Person> personMap = new OptMap<>();
    personMap.find(PERSON_NAME)
      .flatMap(Person::getAddress)
      .flatMap(Address::getCity)
      .ifPresent(value -> Assert.fail("Should not be here"));
}

@org.junit.Test
public void testOptional_Monad_Found() {
    OptMap<String, Person> personMap = new OptMap<>();
    Person person = new Person(new Address(new City(CITY_NAME)));

    personMap.put(PERSON_NAME, person);

    personMap.find(PERSON_NAME)
      .flatMap(Person::getAddress)
      .flatMap(Address::getCity)
      .ifPresent(PersonTest::process);
}


In order to accomplish the above, I had to quickly whip up a couple of new classes, Address and City. I also lifted the OptMap idea from Pierre-Yves' excellent article. My biggest stumbling block in getting these new test cases to compile was needing to make the PersonTest "process" method static:

public static void process(City city) {
    Assert.assertEquals(CITY_NAME, city.name);
}


If I've held your interest this long, I suggest you go and read Pierre-Yves' "What's wrong" article. Hopefully this code I'm providing will help you understand it better.

References

Get the Java IDE that understands code & makes developing enjoyable. Level up your code with IntelliJ IDEA. Download the free trial.

Topics:
java 8 lambda expressions ,java 8 optional ,java ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}