Java8 Optional详解

  • Post author:
  • Post category:java


根据Oracle文档,Optional是一个容器对象,可以包含也可以不包含非null值。Optional在Java 8中引入,目的是解决 NullPointerExceptions的问题。本质上,Optional是一个包装器类,其中包含对其他对象的引用。在这种情况下,对象只是指向内存位置的指针,并且也可以指向任何内容。从其它角度看,Optional提供一种类型级解决方案来表示可选值而不是空引用。


在Optional之前


在Java 8之前,程序员将返回null而不是Optional。这种方法有一些缺点。一种是没有明确的方法来表示null可能是一个特殊值。相比之下,在API中返回Optional是明确的声明,其中可能没有值。如果我们要确保不会出现空指针异常,则需要对每个引用进行显式的空检查,如下所示,我们都同意这是很多样板。


    private void getIsoCode( User user){
        if (user != null) {
            Address address = user.getAddress();
            if (address != null) {
                Country country = address.getCountry();
                if (country != null) {
                    String isocode = country.getIsocode();
                    if (isocode != null) {
                        isocode = isocode.toUpperCase();
                    }
                }
            }
        }
    }



Optional使用



Optional.empty();

Optional<String> empty = Optional.empty();
   public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

返回一个空的Optional对象,EMPTY定义如下

private static final Optional<?> EMPTY = new Optional<>();

注意,Optional除了构造函数,无法再进行value的set操作,一旦一个Optional对象建立,就不能再改变了。看起来如果是空的empty对象,那么都应该是EMPTY这个示例,那么我们对Optional对象判空能不能使用optional == EMPTY这种形式。如果所有空Option是单例的话那么是可以的,显然Empty的定义方式决定其是一个单例

private Optional() {
        this.value = null;
    }

value = null的构造也只有创建Empty的地方能够调用

 System.out.println(new ArrayList<>().stream().findFirst()==Optional.empty());

输出结果为true



Optional.of()

返回特定的非空值Optional。

public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }
 private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

要求value不能为空

// Creating an optional using of

String name = "java";

Optional<String> opt = Optional.of(name);

静态方法需要一个非null参数;否则,将引发空指针异常。因此,如果我们不知道参数是否为null,那就是我们使用 ofNullable的时候,下面将对此进行介绍。



Optional.ofNullable

返回描述指定值的Optional,如果非空,则返回空值。

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

可见创建的空对象依然是初始化的静态单例Empty

// Possible null value

 Optional<String> optional = Optional.ofNullable(name());

  private  String  name(){

  String name = "Java";

  return (name.length() > 5) ? name : null;

 }



ifPresent

 public boolean isPresent() {
        return value != null;
    }

根据value是否为空返回bool值

如果存在值,则返回true;反之,返回false。如果所包含的对象不为null,则返回true,反之返回false。通常在对对象执行任何其他操作之前,先在Optional上调用此方法。


无返回值方法

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

如果存在值,则使用该值调用指定的使用者;否则,什么都不做。



get

如果此Optional中存在值,则返回该值,否则抛出 NoSuchElementException。在这之后,我们想要的是存储在Optional中的值,我们可以通过get()来获取它。但是,当该值为null时,此方法将引发异常。这就需要 orElse() 方法来紧急救援。

//get
Optional<String> optional1 = Optional.of("javaone");
if (optional1.isPresent()){ 
  String value = optional1.get();
}



orElse

public T orElse(T other) {
        return value != null ? value : other;
    }

该 orElse() 方法用于检索包装在Optional实例内的值。它采用一个充当默认值的参数。该 orElse() 方法返回包装的值(如果存在)及其参数.




orElseGet

返回值(如果存在);否则,调用other并返回该调用的结果。

该orElseGet() 方法类似于 orElse()。但是,如果没有Optional值,则不采用返回值,而是采用供应商功能接口,该接口将被调用并返回调用的值:

  //orElseGet
        String name = Optional.ofNullable(nullName).orElseGet(() -> "john");



orElseThrow

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

功能类似,如果value,根据exceptionSupplier抛出异常



filter

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

根据传入的的predicate条件,处理value,如果条件满足,返回当前Optional否则,返回空的Optional

 List<String> list = Arrays.asList("tom", "jame", "jerry", "hello");
 System.out.println(list.stream().findFirst().filter(StringUtil::isNullOrEmpty));



map

 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));
        }
    }

对value进行转换,如果为空,返回空的Option

List<String> list = Arrays.asList("tom", "jame", "jerry", "hello");
System.out.println(list.stream().findFirst().map(String::length));



flatMap

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

与map的区别在于,要求用户自己将转换结果转为Optional对象。

      List<String> list = Arrays.asList("tom", "jame", "jerry", "hello");
        System.out.println(list.stream().findFirst().flatMap(it->Optional.of(it.length())));



解决了什么问题

Optional的方法是尝试通过增加构建更具表现力的API的可能性来减少Java系统中空指针异常的情况,这些API解释了有时缺少返回值的可能性。

在Optional,api的限制下,我们无法获取到空的value进行直接操作,这这样大大减少了空指针异常的情况



解决不了什么问题

Optional并不意味着是一种避免所有类型的空指针的机制。例如,它仍然必须测试方法和构造函数的强制输入参数。

例如判空并get一个对象后,Optional不具有传递性,当再操作这个对象的属性时,仍然可能报出空指针异常



何时使用


Optional

的预期用途

主要是作为返回类型

。获取此类型的实例后,可以提取该值(如果存在)或提供其他行为(如果不存在)。

Optional类的一个非常有用的用例是将其与流或返回Optional值以构建流畅的API的其他方法结合。请参见下面的代码段

User user = users.stream().findFirst().orElse(new User("default", "1234"));



何时不要使用

a)不要将其用作类中的字段,因为它不可序列化

如果确实需要序列化包含Optional值的对象,则Jackson库提供了将Optionals视为普通对象的支持。这意味着Jackson将空对象视为空,将具有值的对象视为包含该值的字段。可以在jackson-modules-java8项目中找到此功能。

b)不要将其用作构造函数和方法的参数,因为这会导致不必要的复杂代码。

本文部分内容引用自

https://juejin.cn/post/6844903960050925581



版权声明:本文为qq_37436172原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。