Optional类

Optional

1、简单概述

jdk8中提供了一个很特别的类:Optional类,这个类设计的初衷是为了来解决臭名昭著的NPE问题。下面我们带着这些方法来进行操作

Optional类的方法总结如下所示:

方法 描述
empty 返回一个空的Optional类实例
of 将对象封装到Optional类中去,要求对象不能够为空,否则返回一个NullPointerException
ofNullable 获取得到Optional类封装的对象,如果对象为空,那么返回一个空实例,如果不为空,返回一个封装了对象的Optional实例
filter 如果值存在而且能够满足提供的谓词,就返回包含该值的Optional对象,否则返回一个空的Optional类实例
map 如果Optional类封装的值存在,那么执行map函数中定义的内容
flatMap 如果该值存在,通过Function函数,返回一个Optional类型的值,否则返回一个空的Optional类实例
get 如果值存在,那么将Optional类实例封装的值返回,否则将会抛出NoSuchElementException异常
isPresent 如果存在封装的对象,那么返回true;如果不存在,那么返回false
ifPresent 如果存在封装的对象,那么执行后面的消费方法;如果不存在,那么不做任何事情
orElse 如果Optional实例对象是empty,那么使用默认的值来代替
orElseGet 如果有值,则获取得到将其返回;如果没有值,那么将会使用指定的函数生成的值
orElseThrow 如果有值将其进行返回,否则抛出一个指定的异常

先分别对这些方法来进行测试,然后做一个总结。

2、方法测试

2.1、of方法

    @Test
    public void testOne(){
        String str = null;
        Optional.of(str);
    }

首先先写到这里,那么这里肯定会报错。看下源码:

    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }
		
	// 代码执行到这里来,那么就意味着这里要求一定不能够是空,否则就会报出NPE异常
    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

    public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

所以从这里可以看到,如果不能够保证这里的Value是否为null,那么就不要使用这个方法。这个方法仅仅说是返回了一个Optional的对象而已。而我们想使用这个类来帮助我们来解决NPE问题,如果说我们已经知道了这里的值是一个非空的了,那么我们就没有必要来进行操作了。当然,为了捕获NPE也是可以的,这样子来做满足业务逻辑即可。

所以引入了下面的ofNullable方法

2.2、ofNullable方法

字面意思是可能为null,这个方法就是为了满足上面的方法而来的。我们不知道value是不是为null的情形,可以来使用这个方法来进行解决。

@Test
public void testThree(){
    //为指定的值创建Optional对象,不管所传入的值为null不为null,创建的时候都不会报错
    Optional<String> nullOptional1 = Optional.ofNullable(null);
    Optional<String> nullOptional2 = Optional.ofNullable("lisi");
}

那么看下源码:

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

如果是空的,那么就返回一个Optional类型的空对象,也就是说值为空的,拿到这个对象毫无意义。

如果不是空的,那么返回一个包装了这个value的Optioal对象,这个value是一个Optional对象,但是这个对象里面的值是null。

    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
	
	// 调用了empty方法之后,返回了一个Optional对象,Optional对象中的value是一个null
	private static final Optional<?> EMPTY = new Optional<>();

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

但是获取得到了这样的一个Optional类对象之后,肯定是需要来对里面的值来进行操作的。那么如何来确定这个值是不是null,这是一个问题。所以针对这个对象引入了下面的方法

2.3、ifPresent方法

字面意思是如果有值,该怎么来进行操作。

    @Test
    public void testOne(){
        String str = "null";
        Optional<String> str1 = Optional.ofNullable(str);
        // 如果值不是为空的,那么就可以直接在这个里面来进行处理
        str1.ifPresent(s -> System.out.println(str+"hello"));
    }

如果不是空的,那么就进行ifPresent方法中的操作。这里可以看下对应的源码:

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

看到了这个Consumer接口,就什么都明白了。一种消费型方法,没有返回值的。

但是这个时候应该考虑到另外一个问题,如果说这个value是空的,那么又该来如何进行操作?

这又引出来了另外一个方法orElse

2.4、orElse方法

字面意思否则就会怎么样?

看一段代码:

    @Test
    public void testOne(){
        Optional<Object> empty = Optional.empty();
        System.out.println(empty.orElse("hello"));
    }

如果Optional对象中的值是空的,那么该怎么来进行处理??这里使用了orElse方法来给value进行赋值,并返回来这个值。看下源码:

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

可以看到如果值不为空,那么就返回Optional这个对象中的值;如果是空的,那么就返回补偿代码补偿的值。

那么再来一段代码:

    @Test
    public void testOne(){
        Student student = Student.builder().id(1).name("hello").build();
        student.setId(Optional.ofNullable(student.getId()).orElse(12));
        System.out.println(student);  // Student(id=1, name=hello)
    }

那么继续,下面接着不需要来对id来进行赋值:

    @Test
    public void testOne(){
        Student student = Student.builder().name("hello").build();
        System.out.println(student.getId()==null);
		student.setId(Optional.ofNullable(student.getId()).orElse(12));
        System.out.println(student);  // Student(id=12, name=hello)
    }

这个方法侧重于对一个Optional中的value来进行判断,然后如果orElse中方法判断成功,那么才会执行对应的方法;如果有着对应的值,那么就不会执行。其实这里可以深究一下这段代码:

Optional.ofNullable(student.getId()).orElse(12)

看下对应的源码:

    // 这里返回的是一个empty(),也就是Optional<null>对象
	public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

	// 然后调用orElse方法,这个对象就是Optional<null>中的value=null,那么这里走other,然后返回
	public T orElse(T other) {
        return value != null ? value : other;
    }

所以得到了最终的值之后,然后返回回去,重新给id赋值。

2.5、orElseGet方法

再拿上面的案例来举例子:

    @Test
    public void testFour(){
        Student student = Student.builder().id(1).name("hello").build();
        Integer integer = Optional.ofNullable(student.getId()).orElseGet(() -> 2);
        System.out.println(integer);  // 1
    }

那么把这里的id去掉:

    @Test
    public void testFour(){
        Student student = Student.builder().name("hello").build();
        Integer integer = Optional.ofNullable(student.getId()).orElseGet(() -> 2);
        System.out.println(integer);  // 2
    }

这里可以发生,当id是空的时候,即使返回的对象是一个Optional的值,也是可以获取得到后面的值的。

那么和orElse源码来进行对比一下:

orElseGet

    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }

orElse:

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

但是这里用了一个Supplier接口来进行操作,感觉和orElse差不多的存在。可能是没有用到具体的场景吧。

2.6 orElseThrow

字面意思"不然抛出一个异常"。

demo1:

// 以前的参数校验:
if(null==xx){
    throw new RuntimeException("参数为空");
}
// 使用Optional
Optional.ofNullable(xx).orElseThrow(n->{
    return New RuntimeExeception("参数为空");
})

demo2:

Optional<String> stringOptional = Optional.of("张三");
System.out.println(stringOptional.orElseThrow(CustomException::new));

Optional<String> emptyOptional = Optional.empty();
System.out.println(emptyOptional.orElseThrow(CustomException::new));

private static class CustomException extends RuntimeException {
   private static final long serialVersionUID = -4399699891687593264L;

   public CustomException() {
       super("参数为空");
   }

   public CustomException(String message) {
      super(message);
   }
  }

这里自定义异常的时候可以来进行使用。

2.7、isPresent方法

是否有这个value值?这里只是用来做一个判断而已。

    @Test
    public void testFour(){
        Student student = Student.builder().name("hello").build();
        boolean present = Optional.ofNullable(student).isPresent();
        System.out.println(present);  // true
    }

再看一下:

    @Test
    public void testFour(){
        Student student = Student.builder().id(1).name("hello").build();
        boolean present = Optional.ofNullable(student).isPresent();
        System.out.println(present); // false
    }

这里仅仅是用来做一个判断的条件而已。

2.8、filter方法

过滤方法,看到了这个方法不禁想起来了Stream流中的filter方法,二者是相同的存在。直接看下源码即可。

    public Optional<T> filter(Predicate<? super T> predicate) {
        // 看到了这句话就不想往下看了。如果是空,直接跑出来空指针异常。
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

可以看到这里也是对非空值的处理:

Optional<String> stringOptional = Optional.of("zhangsan");
System.out.println(stringOptional.filter(e -> e.length() > 5).orElse("王五"));
stringOptional = Optional.empty();
System.out.println(stringOptional.filter(e -> e.length() > 5).orElse("lisi"));

2.9、map方法

如果有值,那么就执行对应的map中的函数

//map方法执行传入的lambda表达式参数对Optional实例的值进行修改,修改后的返回值仍然是一个Optional对象
Optional<String> stringOptional = Optional.of("zhangsan");
System.out.println(stringOptional.map(e -> e.toUpperCase()).orElse("失败"));

stringOptional = Optional.empty();
System.out.println(stringOptional.map(e -> e.toUpperCase()).orElse("失败"));

2.10、flatMap方法

如果创建的Optional中的值存在,就对该值执行提供的Function函数调用,返回一个Optional类型的值,否
则就返回一个空的Optional对象。flatMap与map(Funtion)方法类似,区别在于flatMap中的mapper返回
值必须是Optional,map方法的mapping函数返回值可以是任何类型T。调用结束时,flatMap不会对结果
用Optional封装。

//map方法中的lambda表达式返回值可以是任意类型,在map函数返回之前会包装为Optional。 
//但flatMap方法中的lambda表达式返回值必须是Optionl实例
Optional<String> stringOptional = Optional.of("zhangsan");
System.out.println(stringOptional.flatMap(e -> Optional.of("lisi")).orElse("失败"));

stringOptional = Optional.empty();
System.out.println(stringOptional.flatMap(e -> Optional.empty()).orElse("失败"));

各自的侧重点不同而已。

3、图解

3.0、Optional空对象

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

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

可以看到这里仅仅只是创建了一个Optional对象,而内部的value是为null的。

而optional类对象主要是用来操作该对象的value值。

3.1、of方法图

    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

	private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

3.2、ofNullable方法图

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

那么对照着上面的两种情况,就对应着两个对象:

可能是这两个之间的任何一种情况,所以我们需要来对后面的值来做处理。

3.3、ifPresent方法图

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

如果值不是空的,那么就会对这里面的值来进行操作。

3.4、orElse方法

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

对应的图解:

如果是空的,那么走orElse中的逻辑。

3.5、orElseGet方法图

    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }

3.6、orElseThrow方法图

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

如果对象是为空的,那么就跑出来异常,而这个异常是自己来定义的,实现Throwable接口及其子类即可。只要是空,抛出对应的异常即可。

posted @ 2021-05-12 20:52  雩娄的木子  阅读(480)  评论(0编辑  收藏  举报