A wise man once said that you are not a real Java programmer until you have handled nullpointer exceptions. This is a joke, of course, but null-pointer exceptions are a source of errors in many programs. Thus, in Java 8, java.util.Optional was introduced. The Optional represents data that may or may not be available and can be used to alleviate the problem of null pointer exceptions.

In short, Optional is used to avoid this code:

String version = "UNKNOWN";
if(computer ! =null){
  Soundcard soundcard = computer.getSoundcard();
  if(soundcard ! =null){
    USB usb = soundcard.getUSB();
    if(usb ! =null){ version = usb.getVersion(); }}}Copy the code

What if I call it Optional? It goes something like this:

String version = computer.flatMap(Computer::getSoundcard)
                         .flatMap(Soundcard::getUSB)
                         .map(USB::getVersion)
                         .orElse("UNKNOWN");
Copy the code

In fact, Optional is Maybe in functional programming.

The following example is in optionaltest.java.

create

There are three ways to create Optional: empty, of, and ofNullable.

empty

Empty creates an empty Optional

@Test
public void create_optional_with_empty(a) {
    Optional<String> empty = Optional.empty();
    assertFalse(empty.isPresent());
}
Copy the code

of

Of is used to create a non-empty Optional:

@Test
public void create_optional_with_of(a) {
    Optional<String> java = Optional.of("Java");
    assertTrue(java.isPresent());
}
Copy the code

However, the argument cannot be null, otherwise a null pointer exception will be thrown:

@Test(expected = NullPointerException.class)
public void create_optional_with_of_with_null(a) {
    Optional.of(null);
}
Copy the code

ofNullable

OfNullable is used to create an Optional that may be empty:

@Test
public void create_optional_with_ofNullable(a) {
    Optional<String> java = Optional.ofNullable("Java");
    assertTrue(java.isPresent());

    Optional<Object> o = Optional.ofNullable(null);
    assertFalse(o.isPresent());
}
Copy the code

Check whether the value exists

You can use isPresent and isEmpty to check whether the Optional value isEmpty.

isPresent

Return true if Optional is not null, false otherwise.

@Test
public void check_optional_with_isPresent(a) {
    Optional<String> java = Optional.ofNullable("java");
    Optional<Object> aNull = Optional.ofNullable(null);

    assertTrue(java.isPresent());
    assertFalse(aNull.isPresent());
}
Copy the code

isEmpty

You can start using isEmpty in Java 11.

IsEmpty is the opposite of isPresent and returns true if null.

@Test
public void check_optional_with_isEmpty(a) {
    Optional<String> java = Optional.ofNullable("java");
    Optional<Object> aNull = Optional.ofNullable(null);

    assertFalse(java.isEmpty());
    assertTrue(aNull.isEmpty());
}
Copy the code

Condition action

Conditional actions include ifPresent, orElse, orElseGet, orElseThrow, or, and ifPresentOrElse. They are executed depending on whether the Optional value is null.

To avoid null-pointer exceptions, we often write the following code:

if(name ! =null){
    System.out.println(name.length);
}
Copy the code

The Optional uses a functional alternative to this.

ifPresent

IfPresent accepts a Consumer, called if the Optional value is not null, and accepts the value of Optional.

@Test
public void condition_action_ifPresent(a) {
    Optional<String> java = Optional.ofNullable("java");
    java.ifPresent((value) -> System.out.println("ifPresent accept " + value));

    Optional<Object> aNull = Optional.ofNullable(null);
    aNull.ifPresent(value -> System.out.println("this will never execute"));
}
Copy the code

orElse

OrElse fires when the Optional value is null. It takes one argument as the default for Optional.

@Test
public void condition_action_orElse(a) {
    assertTrue(Optional.ofNullable("java").orElse("javascript").equals("java"));
    assertTrue(Optional.ofNullable(null).orElse("java").equals("java"));
}
Copy the code

orElseGet

OrElseGet is similar to orElse, but orElseGet accepts an Supplier, and the value returned by the Supplier is the default for Optional.

@Test
public void condition_action_orElseGet(a) {
    assertTrue(Optional.ofNullable("java").orElseGet(() -> "javascript").equals("java"));
    assertTrue(Optional.ofNullable(null).orElseGet(() -> "java").equals("java"));
}
Copy the code

The difference between orElse and orElseGet

OrElse and orElseGet have different function signatures, but if we wanted to use the same function return value as the default for Optional, we would probably do this:

public String getDefaultName(a) {
    System.out.println("You got a default name");
    return "default";
}
    
@Test
public void difference_between_orElse_and_orElseGet(a) {
    Optional<String> java = Optional.of("java");

    System.out.println("orElse:");
    assertEquals("java", java.orElse(getDefaultName()));
    System.out.println("orElseGet:");
    assertEquals("java", java.orElseGet(this::getDefaultName));
}
Copy the code

If Java is null, orElse and orElseGet are no different. GetDefaultName executes and returns the Optional default value.

In the example above, when Java is not null, orElse’s getDefaultName is still executed, but orElseGet is not. Output:

orElse:
You got a default name
orElseGet:
Copy the code

Be aware when there are side effects or time-consuming operations in getDefaultName.

orElseThrow

OrElseThrow, like orElse, is thrown when the Optional value is null, but it throws the specified exception:

@Test(expected = IllegalArgumentException.class)
public void condition_action_orElseThrow(a) {
    Optional.ofNullable(null).orElseThrow(IllegalArgumentException::new);
}
Copy the code

or

Or is a new method in Java 9. Much like orElseGet, or accepts an Supplier, but returns a new Optional.

@Test
public void condition_or_optional(a) {
    Optional<String> java = Optional.of("java")
                                    .or(() -> Optional.of("javascript"));
    Optional<Object> java1 = Optional.empty()
                                     .or(() -> Optional.of("java"));
    assertEquals("java", java.get());
    assertEquals("java", java1.get());
}
Copy the code

ifPresentOrElse

IfPresentOrElse is a new method in Java 9. IfPresent is just like the if-else in imperative programming. It takes two arguments, the first one being Consumer, which is called if Optional has a value, and the second being Runnable, which is called if Optional has no value:

@Test
public void condition_ifPresentOrElse(a) {
    // value is java
    Optional.of("java")
            .ifPresentOrElse(value -> System.out.println("value is " + value), () -> System.out.println("ooops"));

    // ooops
    Optional.empty()
            .ifPresentOrElse(value -> System.out.println("value is " + value), () -> System.out.println("ooops"));
}
Copy the code

Get the value

Optional provides a get method to get a value, but get can only be used if Optional has a value, otherwise NoSuchElementException will be thrown:

@Test
public void get_optional_with_of(a) {
    Optional<String> java = Optional.of("Java");
    assertEquals("java", java.get());
}

@Test(expected = NoSuchElementException.class)
public void get_optional_with_of_with_null(a) {
    Optional.empty().get();
}
Copy the code

Verify the values

The filter method validates Optional. It takes a Predicate as an argument. If Optional is null or Predicate fails, return empty. Otherwise, the Optional is returned.

@Test
public void test_optional_by_filter(a) {
    Integer nullYear = null;
    Optional<Integer> integer = Optional.ofNullable(nullYear)
                                        .filter(value -> value == 2018);
    assertEquals(Optional.empty(), integer);

    Integer year = 2019;
    Optional<Integer> integer1 = Optional.ofNullable(year)
                                         .filter(value -> value == 2018);
    assertEquals(Optional.empty(), integer1);

    Optional<Integer> integer2 = Optional.ofNullable(year)
                                         .filter(value -> value == 2019);
    assertEquals("Optional[2019]", integer2.toString());
}
Copy the code

A filter saves a lot of boilerplate code compared to a traditional if, such as:

public boolean priceIsInRange1(Modem modem) {
    boolean isInRange = false;
 
    if(modem ! =null&& modem.getPrice() ! =null
      && (modem.getPrice() >= 10
        && modem.getPrice() <= 15)) {
 
        isInRange = true;
    }
    return isInRange;
}
Copy the code

Use Optional to implement the same approach:

public boolean priceIsInRange2(Modem modem2) {
     return Optional.ofNullable(modem2)
       .map(Modem::getPrice)
       .filter(p -> p >= 10)
       .filter(p -> p <= 15)
       .isPresent();
}
Copy the code

Processing value

There are map and flatMap ways to process values.

map

Use map to process the value in Optional and return it.

@Test
public void map_optional(a) {
    Optional<String> java = Optional.of("java");
    String result = java.map(String::toUpperCase).orElse("");
    assertEquals("JAVA", result);
}
Copy the code

flatMap

The mapper passed to flatMap requires that the return value be a subclass of Optional. You can customize the wrapper class.

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public Optional<String> getName(a) {
        returnOptional.ofNullable(name); }}@Test
public void flatMap_optional(a) {
    Person person = new Person("john");
    Optional<Person> personOptional = Optional.of(person);

    String byMap = personOptional.map(Person::getName)
                                 // You need to open the package manually
                                 .orElse(Optional.empty())
                                 .orElse("");

    String byFlatMap = personOptional.flatMap(Person::getName)
                                     .orElse("");

    assertEquals("john", byMap);
    assertEquals("john", byFlatMap);
}
Copy the code

Flow operation

In Java 9, the stream method has been added. You can create a stream for the Optional and then use all the methods on the stream.

If Optional is empty, create a stream of Empty.

@Test
public void treat_optional_as_stream(a) {
    List<String> collect = Optional.of("java")
                                   .stream()
                                   .map(value -> value.concat("script"))
                                   .collect(Collectors.toList());

    assertArrayEquals(new String[]{"javascript"}, collect.toArray());


    // empty optional
    Optional<String> value = Optional.empty();
    List<String> emptyStream = value.stream()
                                    .map(String::toUpperCase)
                                    .collect(Collectors.toList());

    assertEquals(0, emptyStream.size());
}
Copy the code

Stram can be used to filter Optional values that are not null:

@Test
public void filter_empty_by_stream(a) {
    List<Optional<String>> languages = List.of(Optional.of("java"), Optional.empty(), Optional.empty(), Optional.of("javascript"));
    List<String> collect = languages.stream()
                                    .flatMap(Optional::stream)
                                    .collect(Collectors.toList());

    assertArrayEquals(new String[]{"java"."javascript"}, collect.toArray());
}
Copy the code

reference

  • Tired of Null Pointer Exceptions? Consider Using Java SE 8’s Optional!
  • Guide To Java 8 Optional
  • Java 9 Optional API Additions
  • Filtering a Stream of Optionals in Java