Practical Functional Java

Optionals

Jeff Butler

https://github.com/jeffgbutler/practical-functional-java

https://jeffgbutler.github.io/practical-functional-java/

Optionals

Optionals == A (mostly) replacement for null

All code examples in examples.basics.optional.OptionalTest.java

Creating Optionals

          
    @Test
    public void testCreatingOptionals() {
        Optional<String> name = Optional.of("Fred"); // value must be non-null
        assertThat(name.isPresent()).isTrue();

        name = Optional.ofNullable("Fred");  // value may be null
        assertThat(name.isPresent()).isTrue();

        name = Optional.ofNullable(null);
        assertThat(name.isPresent()).isFalse();

        name = Optional.empty();  // always empty
        assertThat(name.isPresent()).isFalse();
    }
          
        

The map method is used to transform an optional if it exists

          
    @Test
    public void testOptionalMap() {
        Optional<String> name = Optional.of("Fred");
        
        Optional<String> upperName = name.map(String::toUpperCase);
        
        assertThat(upperName.isPresent()).isTrue();
        assertThat(upperName.get()).isEqualTo("FRED");
    }
          
        

The map method works on an empty Optional - just returns an empty Optional

          
    @Test
    public void testOptionalMapOnEmpty() {
        Optional<String> emptyName = Optional.empty();  // or Optional.ofNullable(null);
        
        Optional<String> emptyUpperName = emptyName.map(String::toUpperCase);
        
        assertThat(emptyUpperName.isPresent()).isFalse();
        
        try {
            assertThat(emptyUpperName.get()).isEqualTo("FRED");
            fail("found a value on an empty Optional");
        } catch (NoSuchElementException e) {
            // ignore
        }
    }
          
        

The filter method is used to test the value of an optional if present

          
    @Test
    public void testOptionalFilter() {
        Optional<String> name = Optional.of("Fred");
        
        Optional<String> fred = name.filter(s -> s.equals("Fred"));
        Optional<String> notFred = name.filter(s -> !s.equals("Fred"));
        
        assertThat(fred.isPresent()).isTrue();
        assertThat(notFred.isPresent()).isFalse();
    }
          
        

Just like streams, optional methods can be chained

          
    @Test
    public void testOptionalMethodChaining() {
        Optional<String> name = Optional.of("Fred");
        
        Optional<String> upperFred = name.filter(s -> s.equals("Fred"))
                .map(String::toUpperCase);
                
        assertThat(upperFred.isPresent()).isTrue();
        assertThat(upperFred.get()).isEqualTo("FRED");
    }
          
        

The orElse method is used to supply a default value if the optional is empty

          
    @Test
    public void testOptionalOrElse() {
        Optional<String> fred = Optional.of("Fred");
        
        String upperFred = fred.filter(s -> s.equals("Fred"))
                .map(String::toUpperCase)
                .orElse("Unknown");
                
        assertThat(upperFred).isEqualTo("FRED");

        Optional<String> barney = Optional.of("Barney");
        
        upperFred = barney.filter(s -> s.equals("Fred"))
                .map(String::toUpperCase)
                .orElse("Unknown");
                
        assertThat(upperFred).isEqualTo("Unknown");
    }
          
        

The Optional.ifPresent method is for side effects. We don't want side effects, so avoid doing this

          
    @Test
    public void testOptionalIfPresent() {
        Optional<String> name = Optional.of("Fred");

        StringBuilder sb = new StringBuilder();
        name.ifPresent(s -> {
            sb.append(s.toUpperCase());
        });

        assertThat(sb.toString()).isEqualTo("FRED");
    }
          
        

Optionals Summary

Optionals are a solution for dealing with many null issues. Optional.map and Optional.filter can replace a whole class of if statements in your code.

Suggestions for converting to functional style...

  • Never write another setter method
  • Never write another loop - use Streams
  • Never pass nulls around or do null checks - use Optionals
  • Write pure methods
  • Prefer method references - this will force you to write smaller functions

Time to Code!

Create an XML Renderer