JDK1.8新特性---Optional

参考链接:

       在开发过程中,经常会遇到NullPointerException,为了避免空指针异常,我们常常需要进行一些防御式的检查,所以在代码中常常可见if(obj != null) 这样的判断。幸好在JDK1.8中,java为我们提供了一个Optional类,Optional类能让我们省掉繁琐的非空的判断。

 public final class Optional<T> {
     private static final Optional<?> EMPTY = new Optional<>();
 ​
     private final T value;
 ​
     private Optional() {
         this.value = null;
     }
 ​
     public static<T> Optional<T> empty() {
         @SuppressWarnings("unchecked")
         Optional<T> t = (Optional<T>) EMPTY;
         return t;
     }
 ​
     private Optional(T value) {
         this.value = Objects.requireNonNull(value);
     }
     ......
 }

Optional类提供的方法主要如下:

1. Optional.of()方法

作用:把指定的值封装成Optional对象,如果指定的值为null,则抛出空指针异常。

对应源码如下:

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

举例说明:

 // 创建一个值为小崔的String类型的Optional
 Optional ofOptional = Optional.of("小崔");
 System.out.println("ofOptional = " + ofOptional);
 ​
 // 如果我们用of方法创建Optional对象时,所传入的值为null,则抛出NullPointerException如下图所示
 Optional nullOptional = Optional.of(null);
 System.out.println("nullOptional = " + nullOptional);

输出结果:

 ofOptional = Optional[小崔]
 Exception in thread "main" java.lang.NullPointerException
     at java.util.Objects.requireNonNull(Objects.java:203)
     at java.util.Optional.<init>(Optional.java:96)
     at java.util.Optional.of(Optional.java:108)
     at json.Test01.main(Test01.java:50)

2. Optional.ofNullable()方法

作用:把指定的值封装为Optional对象,如果指定的值为null,则创建一个空的Optional对象。

对应源码如下:

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

举例说明:

 // 创建一个值为null的Optional对象
 Optional<Object> o = Optional.ofNullable(null);
 System.out.println("o = " + o);
 ​
 // 创建一个值为小崔的Optional对象
 Optional<String> stringOptional = Optional.ofNullable("小崔");
 System.out.println("stringOptional = " + stringOptional);

输出结果:

 o = Optional.empty
 stringOptional = Optional[小崔]

3. Optional.empty()

作用:创建一个空的Optional对象

对应源码如下:

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

举例说明:

 Optional<Object> empty = Optional.empty();
 System.out.println("empty = " + empty);

输出结果:

 empty = Optional.empty

4. Optional.get()

作用:如果创建的Optional中有值存在,则返回此值,否则抛出NoSuchElementException。

对应源码如下:

 public T get() {
     if (value == null) {
         throw new NoSuchElementException("No value present");
     }
     return value;
 }

举例说明:

 Optional<String> o = Optional.of("小崔");
 String o1 = o.get();
 System.out.println("o1 = " + o1);
 ​
 // Optional<Object> o2 = Optional.of(null);
 // Optional<Object> o2 = Optional.ofNullable(null);
 Optional<Object> o2 = Optional.empty();
 Object o3 = o2.get();
 System.out.println("o3 = " + o3);

输出结果:

 o1 = 小崔
 Exception in thread "main" java.lang.NullPointerException
     at java.util.Objects.requireNonNull(Objects.java:203)
     at java.util.Optional.<init>(Optional.java:96)
     at java.util.Optional.of(Optional.java:108)
     at json.Test01.main(Test01.java:68)

5. Optional.orElse()

作用:如果创建的Optional中有值存在,则返回此值,否则返回一个默认值.

对应源码如下:

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

举例说明1:

 Optional<String> stringOptional = Optional.of("小崔");
 String newOptional = stringOptional.orElse("真棒");
 System.out.println("newOptional = " + newOptional);
 ​
 Optional<Object> empty = Optional.empty();
 Object orElse = empty.orElse("加油");
 System.out.println("orElse = " + orElse);

输出结果:

 newOptional = 小崔
 orElse = 加油

需要注意的是:不论容器是否为空,只要调用该方法,则对象other一定存在。

       如果调用的是方法的话,不论容器是否为空方法始终会执行,所以orElse推荐场景为设置具体值,调用具体方法使用orElseGet。

举例说明2:Optional.ofNullable(m1()).orElse(m2()),m1结果非空还是会执行m2!

所以,如果orElse()中的计算或其他处理业务很多时,推荐使用orElseGet():

6.orElseGet()

作用:如果创建的Optional中有值存在,则返回此值,否则返回一个由Supplier接口生成的值。

对应源码如下:

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

举例说明:

 Optional<String> stringOptional = Optional.of("小崔");
 // orElseGet与orElse方法类似,区别在于orElse传入的是默认值,而orElseGet可以接受一个lambda表达式生成默认值。
 String orElseGet = stringOptional.orElseGet(() -> "真棒");
 System.out.println("orElseGet = " + orElseGet);
 ​
 Optional<String> emptyOptional = Optional.empty();
 String orElseGet2 = emptyOptional.orElseGet(() -> "真棒");
 System.out.println("orElseGet2 = " + orElseGet2);

输出结果:

 orElseGet = 小崔
 orElseGet2 = 真棒

7. orElseThrow()

作用:如果创建的Optional中有值存在,则返回此值,否则抛出一个有指定的Supplier接口生成的异常。

对应源码如下:

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

举例说明:

 public class Test01 {
     public static void main(String[] args) {
         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);
         }
     }
 }

输出结果:

 小崔
 Exception in thread "main" json.Test01$CustomException: 自定义异常
     at java.util.Optional.orElseThrow(Optional.java:290)
     at json.Test01.main(Test01.java:95)

8. filter()

作用:如果创建的Optional中的值满足filter中的条件,则返回包含该值的Optional对象,否则返回一个空的Optional对象。

对应源码如下:

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

举例说明:

 Optional<Integer> stringOptional1 = Optional.of(12345);
 System.out.println(stringOptional1.filter(e -> e % 2 == 0));
 ​
 Optional<Integer> stringOptional2 = Optional.of(12345);
 System.out.println(stringOptional2.filter(e -> e % 2 == 1));
 ​
 Optional<String> stringOptional3 = Optional.of("小崔");
 System.out.println(stringOptional3.filter(e->e.length() > 5).orElse("加油"));
 ​
 Optional<String> stringOptional4 = Optional.empty();
 System.out.println(stringOptional4.filter(e -> e.length() > 5).orElse("真棒"));
 ​

输出结果:

 Optional.empty
 Optional[12345]
 加油
 真棒

注意:Optional中的filter方法和Stream中的filter方法是有点不一样的,Stream中的filter方法是对一堆元素进行过滤,而Optional中的filter方法只是对一个元素进行过滤,可以把Optional看成是最多只包含一个元素的Stream。

9. map()

作用:如果创建的Optional中的值存在,对该值执行提供的Function函数调用。

对应源码如下:

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

举例说明:

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

输出结果:

 MRCUI
 真棒

10. flatMap()

作用:如果创建的Optional中的值存在,就对该值执行提供的Function函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象。

       flatMap与map(Funtion)方法类似,区别在于flatMap中的mapper函数返回值必须是Optional,map方法的mapper函数返回值可以是任何类型T。调用结束时,flatMap不会对结果用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));
     }
 }

举例说明1:

 // map方法中的lambda表达式返回值可以是任意类型,在map函数返回之前会包装为Optional,但flatMap方法中的lambda表达式返回值必须是Optional实例
 Optional<String> stringOptional = Optional.of("小崔");
 System.out.println(stringOptional.flatMap(e -> Optional.of("小崔")).orElse("加油"));
 ​
 Optional<Object> objectOptional = Optional.empty();
 System.out.println(objectOptional.flatMap(e -> Optional.empty()).orElse("真棒"));

输出结果:

 小崔
 真棒

举例说明1:

 public class Test01 {
     public static void main(String[] args) {
         // 对于calculate这种函数,使用flatMap的话,返回的结果还是Optional,方便使用
         Optional<Integer> opt = Optional
                 .ofNullable(11)
                 .flatMap(a -> calculate(a));
         System.out.println("opt = " + opt);
 ​
         // 对于calculate这种函数,如果使用map,返回的结果嵌套了Optional
         Optional<Optional<Integer>> opt2 = Optional
                 .ofNullable(11)
                 .map(a -> calculate(a));
         System.out.println("opt2 = " + opt2);
     }
 ​
     public static Optional<Integer> calculate(int input){
         return Optional.of(input * 2);
     }
 }

输出结果:

 opt = Optional[22]
 opt2 = Optional[Optional[22]]

       它存在的必要性:因为在使用中存在很多返回值是Optional的函数,为了在Optional使用这些函数时,返回结果能方便使用,如上可以看出来使用flatMap,对于后续的链式编程非常方便。

举例说明2:如果函数返回所需的对象,则使用map;如果函数返回Optional,则使用flatMap。

 public class Test0918 {
     public static void main(String[] args) {
         Optional<String> s = Optional.of("input");
         System.out.println(s.map(Test0918::getOutput));
        System.out.println(s.flatMap(Test0918::getOutputOpt));
     }
 ​
     static String getOutput(String input) {
         return input == null ? null : "output for " + input;
     }
 ​
     static Optional<String> getOutputOpt(String input) {
         return input == null ? Optional.empty() : Optional.of("output for " + input);
     }
 }

11. isPresent()

作用:如果创建的Optional中的值存在,返回true,否则返回false。

对应源码如下:

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

举例说明:

 Optional stringOptional = Optional.of("小崔");
 System.out.println("stringOptional.isPresent() = " + stringOptional.isPresent());
 ​
 Optional stringOptional2 = Optional.empty();
 System.out.println("stringOptional2.isPresent() = " + stringOptional2.isPresent());
 ​

输出结果:

 stringOptional.isPresent() = true
 stringOptional2.isPresent() = false

12. ifPresent()

作用:如果创建的Optional中的值存在,则执行该方法的调用,否则什么也不做。

对应源码如下:

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

举例说明:

 Optional stringOptional = Optional.of("小崔");
 stringOptional.ifPresent(e-> System.out.println("stringOptional:"+e));
 ​
 Optional stringOptional2 = Optional.empty();
 stringOptional2.ifPresent(e-> System.out.println("stringOptional2:"+e));

输出结果:存在时有值输出,不存在时无输出

 stringOptional:小崔
 ​

13. 方法综合调用案例

13.1 案例1:集合判空

 public static void main(String[] args) {
     List<String> list = null;
     list.forEach(x -> System.out.println(x));
 }

上面例子中,如果list先前没有进行判空处理,就会报空指针异常。

 Exception in thread "main" java.lang.NullPointerException
     at json.Test01.main(Test01.java:15)

一般的判空处理都是通过if来实现的,但是jdk1.8提供了更优雅的处理方式,解决方式如下:

 public static void main(String[] args) {
     List<String> list = null;
     List<String> newList = Optional.ofNullable(list).orElse(Lists.newArrayList());
     newList.forEach(x -> System.out.println(x));
 }
 ​
 ​
 -----------------注意--------------------
 使用Lists.newArrayList()前需要引入guava依赖:
 <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
       <version>28.1-jre</version>
 </dependency>
 才能导入Lists:import` `com.google.common.collect.Lists;
     使用Lists.newArrayList()和new ArrayList()的区别主要在于使用Lists.newArrayList()能够自动推导泛型类型,但在JDK1.7中有了diamond操作符 <>也可推导泛型类型,例如:
 ​
 List<String> list = new ArrayList<String>();    // 老版本写法
 List<String> list = new ArrayList<>();          // JDK1.7及以后的写法:省略了后面的泛型
 ArrayList<Object> objects = Lists.newArrayList(); // 使用Lists.newArrayList()新建ArrayList
 // 对于上例中的Optional.ofNullable(list).orElse(Lists.newArrayList())可自动推导出泛型String

       其中,Optional.ofNullable(list).orElse(Lists.newArrayList())的逻辑思路是:先判断list是否为空,如果不为空直接将list赋值给newList;如果list为空,就会创建一个新的空对象集合赋值给newList,这样可以使list集合永远不为空,避免了空指针异常的发生,Optional.ofNullable()源码如下:

 //静态变量 empty
 private static final Optional<?> EMPTY = new Optional<>();
  
 //如果对象为空,执行empty()方法;不为空,执行of(value)方法
 public static <T> Optional<T> ofNullable(T value) {
     return value == null ? empty() : of(value);
 }
  
 public static<T> Optional<T> empty() {
     @SuppressWarnings("unchecked")
     Optional<T> t = (Optional<T>) EMPTY;
     return t;
 }
 ​
 public static <T> Optional<T> of(T value) {
     return new Optional<>(value);
 }

执行逻辑如下:

  1. 先执行Optional.ofNullable()方法,判断泛型对象T的value是否为空,若为空,执行empty()方法;若不为空,执行of(value)方法;

  2. 对于empty()方法,初始化一个泛型对象为T的Optional空对象,注意此处的空对象不等同于null;

  3. 对于of(value)方法,将泛型对象用于Optional构造方法的参数上,返回一个有值的对象。

通过上面两步,保证了Optional对象不为null,避免了空指针异常。

通过上面的分析可总结Optional.ofNullable(..).orElse(..)的使用方法为:

结果 = Optional.ofNullable(值1).orElse(值2)

一个底层包装好的三元运算符,值1不为空时,取值1;值1为空时,取值2。

但要注意如下:

(1)不能用于字符串判空,“ ”空字符串判定为非空

 String s1 = null;
 String s2 = Optional.ofNullable(s1).orElse("Others");
 System.out.println("s2 = " + s2);// s2 = Others
 ​
 ​
 String s3 = "";
 String s4 = Optional.ofNullable(s3).orElse("Others");
 System.out.println("s4 = " + s4);// s4 = 
 ​
 boolean s1IsEmpty = StringUtils.isEmpty(s1);// import org.apache.commons.lang3.StringUtils;
 System.out.println("s1IsEmpty = " + s1IsEmpty);// s1IsEmpty = true
 boolean s3IsEmpty = StringUtils.isEmpty(s3);
 System.out.println("s3IsEmpty = " + s3IsEmpty);// s3IsEmpty = true
 ​
 补充:
     list.isEmpty();// 判断集合是否为空   List自带的
     CollectionUtil.isEmpty(list);// 判断集合是否为空  Hutool中的CollUtil类中方法
     CollectionUtil.isNotEmpty(list);// 判断集合是否不为空 Hutool中的CollUtil类中方法

注意:使用StringUtils.isEmpty需要引入commons-lang3依赖:

 <dependency>
     <groupId>org.apache.commons</groupId>
     <artifactId>commons-lang3</artifactId>
     <version>3.12.0</version>
 </dependency>

才能导入StringUtils:import org.apache.commons.lang3.StringUtils;

org.springframework.uti包下的StringUtils的使用和org.apache.commons.lang包下StringUtils的使用参见:org.springframework.uti包下的StringUtils的使用和org.apache.commons.lang包下StringUtils的使用_MarkMooer的博客-优快云博客

(2)Optional.ofNullable(m1()).orElse(m2()),m1结果非空还是会执行m2!

所以,如果orElse()中的计算或其他处理业务很多时,推荐使用orElseGet():

13.2 案例2:数据库查询结果判空

在service层中查询一个对象,返回之后判断是否为空并做处理:

 // 查询一个对象
 Member member = memberService.selectByIdNo(request.getCertificateNo());
 // 使用ofNullable加orElseThrow做判断和操作
 Optional.ofNullable(member).orElseThrow(() -> new ServiceException("没有查询的相关数据"));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值