• JDK1.8新特性---Optional


    参考链接:

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

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

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

    1. Optional.of()方法

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

    对应源码如下:

     public static  Optional 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.(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  Optional ofNullable(T value) {
         return value == null ? empty() : of(value);
     }

    举例说明:

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

    输出结果:

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

    3. Optional.empty()

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

    对应源码如下:

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

    举例说明:

     Optional 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 o = Optional.of("小崔");
     String o1 = o.get();
     System.out.println("o1 = " + o1);
     ​
     // Optional o2 = Optional.of(null);
     // Optional o2 = Optional.ofNullable(null);
     Optional 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.(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 stringOptional = Optional.of("小崔");
     String newOptional = stringOptional.orElse("真棒");
     System.out.println("newOptional = " + newOptional);
     ​
     Optional 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 other) {
         return value != null ? value : other.get();
     }

    举例说明:

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

    输出结果:

     orElseGet = 小崔
     orElseGet2 = 真棒

    7. orElseThrow()

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

    对应源码如下:

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

    举例说明:

     public class Test01 {
         public static void main(String[] args) {
             Optional stringOptional = Optional.of("小崔");
             System.out.println(stringOptional.orElseThrow(CustomException::new));
     ​
             Optional 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 filter(Predicate predicate) {
         Objects.requireNonNull(predicate);
         if (!isPresent())
             return this;
         else
             return predicate.test(value) ? this : empty();
     }

    举例说明:

     Optional stringOptional1 = Optional.of(12345);
     System.out.println(stringOptional1.filter(e -> e % 2 == 0));
     ​
     Optional stringOptional2 = Optional.of(12345);
     System.out.println(stringOptional2.filter(e -> e % 2 == 1));
     ​
     Optional stringOptional3 = Optional.of("小崔");
     System.out.println(stringOptional3.filter(e->e.length() > 5).orElse("加油"));
     ​
     Optional 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 Optional map(Function mapper) {
         Objects.requireNonNull(mapper);
         if (!isPresent())
             return empty();
         else {
             return Optional.ofNullable(mapper.apply(value));
         }
     }

    举例说明:

     // map方法执行传入的lambda表达式参数对Optional实例的值进行修改,修改后的返回值仍然是一个Optional对象
     Optional 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 Optional flatMap(Function> mapper) {
         Objects.requireNonNull(mapper);
         if (!isPresent())
             return empty();
         else {
             return Objects.requireNonNull(mapper.apply(value));
         }
     }

    举例说明1:

     // map方法中的lambda表达式返回值可以是任意类型,在map函数返回之前会包装为Optional,但flatMap方法中的lambda表达式返回值必须是Optional实例
     Optional stringOptional = Optional.of("小崔");
     System.out.println(stringOptional.flatMap(e -> Optional.of("小崔")).orElse("加油"));
     ​
     Optional 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 opt = Optional
                     .ofNullable(11)
                     .flatMap(a -> calculate(a));
             System.out.println("opt = " + opt);
     ​
             // 对于calculate这种函数,如果使用map,返回的结果嵌套了Optional
             Optional> opt2 = Optional
                     .ofNullable(11)
                     .map(a -> calculate(a));
             System.out.println("opt2 = " + opt2);
         }
     ​
         public static Optional 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 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 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 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 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 list = null;
         List newList = Optional.ofNullable(list).orElse(Lists.newArrayList());
         newList.forEach(x -> System.out.println(x));
     }
     ​
     ​
     -----------------注意--------------------
     使用Lists.newArrayList()前需要引入guava依赖:
     
           com.google.guava
           guava
           28.1-jre
     
     才能导入Lists:import` `com.google.common.collect.Lists;
         使用Lists.newArrayList()和new ArrayList()的区别主要在于使用Lists.newArrayList()能够自动推导泛型类型,但在JDK1.7中有了diamond操作符 <>也可推导泛型类型,例如:
     ​
     List list = new ArrayList();    // 老版本写法
     List list = new ArrayList<>();          // JDK1.7及以后的写法:省略了后面的泛型
     ArrayList 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  Optional ofNullable(T value) {
         return value == null ? empty() : of(value);
     }
      
     public static Optional empty() {
         @SuppressWarnings("unchecked")
         Optional t = (Optional) EMPTY;
         return t;
     }
     ​
     public static  Optional 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依赖:

     
         org.apache.commons
         commons-lang3
         3.12.0
     

    才能导入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的博客-CSDN博客

    (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("没有查询的相关数据"));
  • 相关阅读:
    XX省水资源管理系统实施方案(终版)
    JSON&redis
    智能优化算法和神经网络,人工神经网络优化算法
    mongoose搭建mqtt客户端
    数字化时代,VR全景如何助力商企抢占市场份额?
    linux0.11-虚拟内存
    golang 协程的实现原理
    2023最新SSM计算机毕业设计选题大全(附源码+LW)之java校园二手物品交易系统051x4
    【管理运筹学】第 7 章 | 图与网络分析(1,图论背景以及基本概念、术语、矩阵表示)
    (附源码)php校园寝室分配查询系统 毕业设计 032027
  • 原文地址:https://blog.csdn.net/xiaocui1995/article/details/126913146