Optional 是什么

Optional 是 JAVA8 的一个新特性。Optional 类位于 java.util包下,那也就是说它其实是一个工具类。

我们看看官方是怎么介绍 Optional的。

image

java.util.Optional是一个可以包含或不可以包含非空值的容器对象,也就是说可以保存类型 为 T 的值,也可以仅仅保存 null 。如果值存在, 则 isPresent()方***返回 true,调用其 get()方***返回该值。

Optional 有什么用

Optional 类的引入主要是为了解决空指针异常(NullPointerException)。在我们平常开发中,我相信各位遇到最多的 Exception 也就是 NullPointerException了。各位可能图简单,可能会编写如下代码:

String cardNo = student.getCard().getCardNo().toUpperCase();

就上述代码出现 NullPointerException异常的情况实在是太多了,如果不想出现NullPointerException异常,那就要显示的进行空判断,如下:

String cardNoUpper = null;
if(student != null){
    Card card = student.getCard();
    if(card != null){
        String cardNo = card.getCardNo();
        if (cardNo != null){
            cardNoUpper = cardNo.toUpperCase();
        }
    }
}

可以看到,只能在每个方法都加入非空检查,这样代码的阅读性和维护性都比较差。为了简化这个过程,我们就可以使用Optional了。Optional提供很多有用的方法,这样我们就可以不用显式的进行空值检测。


创建两个类,先来学习一下 Optional的使用方式和使用方法。

public class Student {

    private String name;
    private Integer age;
    private Double grades;
    // Card
    private Card card;
    //get set toString省略
}

public class Card {

    private String cardNo;
    private String cardName;
}

创建 Optional 实例

创建 Optional 实例的方式有三种,分别是of、empty、ofNullable 三个静态方法。接下来分别介绍一下。

  • Optional.of(T t) : 创建一个 Optional 实例,t 必须非空; @Test public void test1(){ Optional<Student> student = Optional.of(new Student()); Student student1 = student.get(); System.out.println(student1); } 结果:Student{name='null', age=null, grades=null} 复制代码如果 of 方法的参数为 null,会出现空指针异常。该异常发生在创建 Optional 实例的时候。反例:
@Test
public void test1(){
    Optional<Student> student2 = Optional.of(null);
}
image
  • Optional.empty() : 创建一个空的 Optional 实例

如果硬是要创建一个 null 的 Optional 实例,可以使用该方法。

@Test
public void test2(){
    Optional<Student> student = Optional.empty();
    Student student1 = student.get();
    System.out.println(student1);
}
image

该NoSuchElementException异常发生在 get() 方法被调用时。

  • Optional.ofNullable(T t):t 可以为 null。如果非空,使用传入的值构建 Optional;否则返回空的Optional。
@Test
public void test3(){
    Optional<Student> student = Optional.ofNullable(new Student());
    Student student1 = student.get();
    System.out.println(student1);
}
结果:Student{name='null', age=null, grades=null}

如果ofNullable参数传入 null,结果与 empty() 方法一致。源码如下:

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

学会了创建Optional实例的方法,接下来我们看看其他方法。

判断Optional容器中是否包含对象

  • boolean isPresent()·: 判断是否包含值,如果值存在则返回 true,否则返回 false。

实例:

@Test
public void test4(){
    Optional<Student> student = Optional.ofNullable(new Student());
    if(student.isPresent()){
        Student student1 = student.get();
        System.out.println(student1);
    }

    Optional<Student> student2 = Optional.ofNullable(null);
    //取反
    if (!student2.isPresent()) {
        System.out.println("该对象为空值");
    }
}
结果:
Student{name='null', age=null, grades=null}
该对象为空值
  • void ifPresent(Consumer<? super T> consumer) :与上述方法一字之差。如果有值,就执行 accept.accept;否则什么也不做。
public void ifPresent(Consumer<? super T> consumer) {
    if (value != null)
        consumer.accept(value);
}

实例:

  @Test
public void test5(){
    Optional<Student> student = Optional.ofNullable(new Student("新三", 18, 55.0));
    student.ifPresent((x)-> {
        x.setName("李四");
    });
    System.out.println(student.get());
}
结果:Student{name='李四', age=18, grades=55.0}

获取Optional容器的对象

  • T get(): 如果调用对象包含值,返回该值,否则抛 NoSuchElementException异常
  • T orElse(T other) :如果有值则将其返回,否则返回指定的对象

实例:

@Test
public void test6(){

    Optional<Student> op = Optional.ofNullable(null);
    // op 传入null,使用应返回我们指定的对象
    Student st = op.orElse(new Student("新三", 18, 55.0));
    System.out.println(st);

    //传入Student
    Optional<Student> op2 = Optional.ofNullable(new Student());
    Student st2 = op2.orElse(new Student("新三", 18, 55.0));
    System.out.println(st2);
}

Student{name='新三', age=18, grades=55.0}
Student{name='null', age=null, grades=null}
  • T orElseGet(Supplier<? extends T> other) :如果有值则将其返回,否则返回由Supplier函数式接口提供的对象。
@Test
public void test7(){
    Optional<Student> op = Optional.ofNullable(null);
    Student st = op.orElseGet(()->{
        //由于是一个函数式接口,里面可以写逻辑
        Student student = new Student();
        student.setAge(99);
        student.setGrades(888.2);
        student.setName("李四");
        return student;
    });
    System.out.println(st);
}
结果:Student{name='李四', age=99, grades=888.2}k6a8
  • T orElseThrow(Supplier<? extends X> exceptionSupplier) :如果有值则将其返回,否则抛出由Supplier函数式接口提供的异常
@Test
public void test8(){
    Optional<Student> op = Optional.ofNullable(null);
    Student st = op.orElseThrow(() -> new RuntimeException("值不存在"));
    System.out.println(st);
}
image
  • <U> Optional<U> map(Function<? super T, ? extends U> mapper):如果有值对其执行mapper.apply,并返回处理后的Optional。否则返回Optional.empty()。源码如下:
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));
    }
}

实例:

 @Test
 public void test9(){
        Optional<Student> op = Optional.ofNullable(new Student("新,三", 18, 55.0));
        Optional<String[]> s1 = op.map(s -> s.getName())
                                  .map(s -> s.split(","));
        System.out.println(Arrays.toString(s1.get()));
    }
结果:[新, 三]
  • <U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper):与map类似,但是要求参数必须是Optional。
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));
    }
}

实例:

 @Test
public void test10(){
    Optional<Student> op = Optional.ofNullable(new Student("新,三", 18, 55.0));
    Optional<String[]> s1 = op.flatMap(s -> Optional.ofNullable(s.getName())
                              .flatMap(s2 -> Optional.ofNullable(s2.split(",")))
    );
    System.out.println(Arrays.toString(s1.get()));
}
结果:[新, 三]
  • Optional<T> filter(Predicate<? super T> predicate):如果值存在,并且满足断言条件,就返回该值的Optional,否则返回Optional.empty
public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    if (!isPresent())
        return this;
    else
        return predicate.test(value) ? this : empty();
}

实例:

 @Test
public void test11(){
    Optional<Student> optional = Optional.of(new Student("新三", 18, 55.0));
    // 不符合断言条件 返回 Optional.empty
    Optional<Student> opt = optional.filter(s -> {
        return "张".equals(s.getName());
    });
    System.out.println(opt.orElse(new Student("李四", 22, 223.0)));
}

结果:Student{name='李四', age=22, grades=223.0}

到此 Optional的提供的方法到此差不多介绍完毕了,接下来就是完善开篇提到的问题了,怎么使用Optional 来优化一下代码呢?

String cardNoUpper = null;
if(student != null){
    Card card = student.getCard();
    if(card != null){
        String cardNo = card.getCardNo();
        if (cardNo != null){
            cardNoUpper = cardNo.toUpperCase();
        }
    }
}

首先重构一下Student 类内的 getCard方法,返回其 Optional 引用。

public class Student {

    private String name;
    private Integer age;
    private Double grades;
    private Card card;
    // 其余省略。。。
    public Optional<Card> getCard() {
        return Optional.ofNullable(card);
    }

    public void setCard(Card card) {
        this.card = card;
    }
}

准备工作已经完成了,就开始编码吧!

public static void main(String[] args) {
    Student student = new Student("新三", 18, 55.0);
    Card cardObj = new Card("ACafgtc", "工商银行");
    student.setCard(cardObj);
    //Optional<Student> o = Optional.ofNullable(null);
    Optional<Student> o = Optional.ofNullable(student);
    String cardNoUpper = o
            .flatMap(s -> s.getCard())   // 获得 Optional<Card>
            .map(card -> card.getCardNo())    // 获得 Card 内的 cardNo
            .map(cardNo -> cardNo.toUpperCase())
            .orElse("默认值");
    System.out.println(cardNoUpper);
}
结果:ACAFGTC

也可以直接将 Student设置为 null,看会不会出现异常。

public static void main(String[] args) {
    Optional<Student> o = Optional.ofNullable(null);
    String cardNoUpper = o
            .flatMap(s -> s.getCard())   // 获得 Optional<Card>
            .map(card -> card.getCardNo())    // 获得 Card 内的 cardNo
            .map(cardNo -> cardNo.toUpperCase())
            .orElse("默认值");
    System.out.println(cardNoUpper);
}
结果:默认值

总结

对于 Optional这个类,有人觉得没必要去学,因为觉得在工作当中不会使用到它。想法其实是不正确的,虽然我们并不会将自己的方法返回值封装为Optional,但是学习Optional还是很有必要的,不说多的,至少要看得懂,会用。因为现在很多类库在使用Optional封装返回值,比如 Spring Data JPA、Stream API等。

  • 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。