1、Optional使用场景

写代码时为了防止空指针的异常NullPointerException(NPE),一般会对参数进行判空处理,日常开发过程中对参数的判空校验有两种情况:

  • 前端调用后端接口,后端在controller层里的接口对入参进行校验判空,一般会结合@Validated@NotNull注解;
  • 后端之间互相调用接口,出于某些原因未能成功调用接口,接口的返回值为null,此时如果依然对接口返回的对象调用get属性之类的方法会抛空指针异常。

Java8针对第二种空指针异常场景提出了类级别的解决方案:Optional。不用Optional,我们一般会进行如下判空处理:

  1. Object object = getObject();
  2. if (object != null)
  3. {
  4. 业务逻辑,比如object.getProperties();
  5. }
  6. else
  7. {
  8. return;
  9. }

使用Optional,上面那一坨逻辑就可以写成:

  1. Object object = getObject();
  2. Optional.ofNullable(object).ifPresent(业务逻辑,比如object.getProperties());

Optional类会把真正需要的实例,如上面的object给包装成一个Optional对象,Optional对象的value才是实际需要的对象实例,即object。使用Optional进行判空处理写法上要简洁些。

2、创建 Optional 对象

创建Optional对象一般使用Optional类提供的静态方法。

2.1 Optional.empty()

可以使用静态方法 empty() 创建一个空的 Optional 对象:

  1. Optional<Object> optional = Optional.empty();
  2. System.out.println(optional);

打印结果:

  1. Optional.empty

2.2 Optional.of()

可以使用静态方法 of() 创建一个非空的 Optional 对象:

Optional<String> optional = Optional.of("Jerry");
System.out.println(optional);

打印结果:

Optional[Jerry]

注意:Optional.of()里一定要传入参数,否则会抛异常。

2.3 Optional.ofNullablle

可以使用静态方法 ofNullable() 创建一个即可空又可非空的 Optional 对象:

Optional<String> optional = Optional.ofNullable(null);
System.out.println(optional);

String name = "Jerry";
Optional<String> optional1 = Optional.ofNullable(name);
System.out.println(optional1);

打印结果:

Optional.empty
Optional[Jerry]

其实Optional.ofNullable底层也是调用的Optional.of和Optional.empty(),源码如下:

public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

3、获取Optional对象

3.1 get

最直观的获取Optional对象应该是get方法,但其实不然,因为假如 Optional 对象的值为 null,该方法会抛出 NoSuchElementException 异常,这完全与我们使用 Optional 类的初衷相悖。
举例:

public class OptionalDemo {
    public static void main(String[] args) {
        Optional<String> optional = Optional.ofNullable(null);
        String name = optional.get();
        System.out.println(name);
    }
}

结果:
image.png
因此不推荐使用get方法获取optional对象的value。

3.2 orElse

如果Optional实例有值则将其返回,否则返回orElse方法传入的参数值。

public T orElse(T other)

举例:

public class OptionalDemo {
    public static void main(String[] args) {
        // 输出"null时的默认值"
        Optional<String> optional = Optional.ofNullable(null);

        // 输出Jerry
        // Optional<String> optional = Optional.ofNullable("Jerry");
        String name = optional.orElse("null时的默认值");
        System.out.println(name);
    }
}

3.3 orElseGet

orElseGetorElse方法类似,区别在于得到的默认值。orElse方法将传入的泛型值作为默认值,orElseGet方法可以接受Supplier的实现用来生成默认值。
举例:

public class OptionalDemo {
    public static void main(String[] args) {
        // 输出"null value"
        Optional<String> optional = Optional.ofNullable(null);

        // 输出Jerry
        // Optional<String> optional = Optional.ofNullable("Jerry");
        String name = optional.orElseGet(() -> "null value");
        System.out.println(name);
    }
}

orElseorElseGet的区别:

  • 入参不同,orElse接收一个值,orElseGet接收一个lambda表达式,由这个lambda表达式定义如何处理null值;
  • 当Optional对象的value不为null时,orElse(getValue())里的getValue()方法依然会执行,但是不会将执行结果返回,orElseGet(() -> {})里则压根不会执行lambda表达式里的代码(详细例子见参考链接1),因此从这一点看orElseGet()方法的效率更高。

综上,获取Optional对象推荐使用orElseGet()方法。

3.4 orElseThrow

如果Optional对象不为null,直接返回value,否则抛出exceptionSupplier的异常。
源码如下:

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

举例:

public class OptionalDemo {
    public static void main(String[] args) {
        // Optional<String> optional = Optional.ofNullable("jerry");
        Optional<String> optional = Optional.ofNullable(null);
        try {
            String name = optional.orElseThrow(DemoException::new);
            System.out.println(name);
        } catch (DemoException e) {
            e.printStackTrace();
        }
    }
}
package com.Jerry.optional;

public class DemoException extends Throwable{
    public DemoException() {
        super();
    }

    public DemoException(String msg) {
        super(msg);
    }

    @Override
    public String getMessage() {
        return "Here is message";
    }
}

4、非空表达式ifPresent()

ifPresent()方法定义了当Optional对象的value不为null时该怎么继续处理,接收一个Consumer接口,如下:

public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

举例:

public class OptionalDemo {
    public static void main(String[] args) {
        Optional<String> optional = Optional.ofNullable("jerry");
        optional.ifPresent(value -> System.out.println(value.length()));
    }
}

ifPresent()方法这种链式的写法很简洁舒服,注意lambda表达式里直接是value -> {}。

5、Optional的filter和map

Optional的filter和map方法是偏附加功能的方法,在Stream里也能看到这两个方法,是在创建和获取Optional对象这些基本方法外额外提供的拓展方法。

5.1 filter

filter方法接收一个表达式,如果不满足表达式,则返回一个Optional.empty(),满足表达式则返回过滤后的Optional对象。
源码:

public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

举例:Optional对象的value字符串长度大于4的才能返回:

public class OptionalDemo {
    public static void main(String[] args) {
        // 返回null value
        // Optional<String> optional = Optional.ofNullable("Tom");

        // 返回Jerry
        Optional<String> optional = Optional.ofNullable("Jerry");
        // 过滤出value字符串长度大于4的返回
        String name = optional.filter(value -> value.length() > 4).orElse("null value");
        System.out.println(name);
    }
}

当filter里的lambda表达式判断规则有多个时,可以写成这种链式的代码,优雅:

Predicate<String> len6 = pwd -> pwd.length() > 6;
Predicate<String> len10 = pwd -> pwd.length() < 10;

password = "1234567";
opt = Optional.ofNullable(password);
boolean result = opt.filter(len6.and(len10)).isPresent();
System.out.println(result);

5.2 map

map方法可以按照一定的规则将原有 Optional 对象映射为一个新的 Optional 对象,源码如下:

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }

如果Optional对象的value为null直接返回Optional.empty(),不为null返回映射后的Optional对象。
举例:

public class OptionalDemo {
    public static void main(String[] args) {
        // 打印结果为0
        // Optional<String> optional = Optional.ofNullable(null);

        // 映射为value字符串的长度的2倍构成的Optional对象
        //打印结果为10
        Optional<String> optional = Optional.ofNullable("Jerry");
        Optional<Integer> optionalInteger = optional.map(value -> value.length() * 2);
        System.out.println(optionalInteger.orElse(0));
    }
}

6、使用Optional的注意点

  • 当返回结果可能为null时使用Optional;
  • 对public方法中的每个入参都进行校验,这种防御型编程虽然可以保护代码,什么都判空,不仅影响效率,还让代码变得不优雅,现实里写代码时还是在容易出现NPE的地方进行判空,需要进行识别;
  • 避免使用Optional.get(),推荐使用避免使用Optional.orElseGet();
  • 不要将null传给Optional;
  • lambda表达式尽量写成单行,如果逻辑比较复杂,封装在方法里,lambda表达式直接调用方法,这不仅是Optional应遵循的规则,在stream等函数式编程里都应遵循这种规范,这样使代码简洁易读。

为什么有人说Optional影响性能?什么场景下会影响性能?

参考:

建议收藏,史上最佳 Java Optional 指南,没有之一
Java Optional 的 orElse() 和 orElseGet() 的区别
浅析Java Optional使用的最佳实践
在Java中,是不是所有的public方法的参数都需要判空处理?