• 如何更好地使用Java 8的Optional


    Java 8中的Optional<T>

    是一个可以包含或不可以包含非空值的容器对象,在 Stream API中很多地方也都使用到了Optional。

      java中非常讨厌的一点就是nullpoint,碰到空指针就会出错抛Exception,然后需要逐行检查是哪个对象为空,带来大量的不必要精力损耗,抛出NPE错误不是用户操作的错误,而是开发人员的错误,应该被避免,那么只能在每个方法中加入非空检查,阅读性和维护性都比较差。

      如下面这个代码的手工非空检查:

    public void addAddressToCustomer(Customer customer, Address newAddress){
     if ( customer == null || newAddress == null)
     return;
     
     if ( customer.getAddresses() == null ){
       customer.setAddresses ( new ArrayList&lt;&gt;());
     }
     customer.addAddress(newAddress);
    }

      另外还有一些开发人员喜欢通过非空检查来实现业务逻辑,空对象不应该用来决定系统的行为,它们是意外的Exceptional值,应当被看成是错误,而不是业务逻辑状态。

      当我们一个方法返回List集合时,应该总是返回一个空的List,而不是Null,这就允许调用者能够遍历它而不必检查Null,否则就抛出NPE。

      但是如果我们根据标识键ID查询数据库,没有查到,需要返回一个空对象怎么办?有人建议抛出Exception,其实这不符合函数方法一进一出的原则,变成一个函数方法有两个返回,一个是正常返回,一个出错Exception,函数式编程范式告诫我们不要轻易抛Exception。

      这时Java 8的Optional就发挥作用了,允许我们返回一个空的对象。

      Optional<T>有方法 isPresent() 和 get() 是用来检查其包含的对象是否为空或不是,然后返回它,如:

    Optional<SomeType> someValue = someMethod();

    if (someValue.isPresent()) { // check

        someValue.get().someOtherMethod(); // retrieve and call

    }

      但是这种用法并不能体现Java 8的全部好处,你可以将Optional看成是需要使用某个T值的方法之间某种中间人或者协调者Mediator,而不只是一个普通对象的包装器。

      如果你有一个值返回类型T,你有一个方法需要使用这个值,那么你可以让 Optional<T> 处于中间,确保它们之间交互进行,而不必要人工干预。

      这样,协调者Optional<T>能够照顾T的值提供给你的方法作为输入参数,在这种情况下,如果T是空,可以确保不会出错,这样在T值为空时也可以让一切都正常运作,你也可以让Optional<T>执行其他动作,如执行一段代码块等等,这样它就实际上是语言机制的很好的补充。

      下面这个案例涉及到Lambda表达式 方法引用,是将单词流中第一个以"L"开始单词取出,作为返回结果是一个Optional<String>。

    使用ifPresent()

      这个案例的代码如下:

    Stream<string> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

    Optional<string> longest = names

                                    .filter(name -> name.startsWith("L"))

                                    .findFirst();

    longest.ifPresent(name -> {

                String s = name.toUpperCase();

                System.out.println("The longest name is "+ s);

            });

      这里ifPresent() 是将一个Lambda表达式作为输入,T值如果不为空将传入这个lambda。那么这个lambda将不为空的单词转为大写输出显示。在前面names单词流寻找结果中,有可能找不到开始字母为L的单词,返回为空,也可能找到不为空,这两种情况都传入lambda中,无需我们打开盒子自己编写代码来判断,它自动帮助我们完成了,无需人工干预。

    使用map()

      如果你想从Optional<T>中返回一个值怎么办?使用 map(),如下:

    Stream<string> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

    Optional<string> longest = names

                                    .filter(name -> name.startsWith("L"))

                                    .findFirst();

    Optional<string> lNameInCaps = longest.map(String::toUpperCase);

      使用Optional<T>的map方法能够返回另外一个Optional,如上面的 LnameInCaps,因为传入map()的参数值也许会导致一个空值。

    使用orElse()

      如果在T可能空时你需要一个值的话,那么可以使用 orElse(),它能在T值存在的情况下返回这个值,否则返回输入值。

    Stream<String> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

    Optional<String> longest = names

                                    .filter(name -> name.startsWith("Q"))

                                    .findFirst();

     String alternate = longest.orElse("Nimrod");

     System.out.println(alternate); //prints out "Nimrod"

    使用orElseGet()

      orElseGet() 方法类似于orElse(),但是不是直接返回输入参数,而是调用输入参数,返回调用的结果,这个输入参数通常是lambda:

    Stream<String> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

    Optional<String> longest = names

                                    .filter(name -> name.startsWith("Q"))

                                    .findFirst();

     String alternate = longest.orElseGet(() -> {

                // perform some interesting code operation

                // then return the alternate value.

                return "Nimrod";

     });

     System.out.println(alternate);

    使用 orElseThrow()

      orElseThrow()是在当遭遇Null时,决定抛出哪个Exception时使用:

    Stream<String> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

     Optional<String> longest = names

                                     .filter(name -> name.startsWith("Q"))

                                     .findFirst();

    longest.orElseThrow(NoSuchElementStartingWithQException::new);

    总结,你能创建下面三种类型的Optional<T>:

    Optional<SomeType> getSomeValue() {

    // 返回一个空的Optional类型;

    return Optional.empty();

    }

    Optional<SomeType> getSomeValue() {

    SomeType value = ...;

    // 使用这个方法,值不可以为空,否则抛exception

    return Optional.of(value);

    }

    Optional<SomeType> getSomeValue() {

    SomeType value = ...;

    // 使用这个方法,值可以为空,如果为空返回Optional.empty

    return Optional.ofNullable(value);

    // usage

    Optional<SomeType> someType = getSomeValue();

  • 相关阅读:
    python模块整理2-sys模块 分类: python Module 2013-09-13 16:49 563人阅读 评论(0) 收藏
    sys常用模块小探 分类: python Module 2013-09-13 16:42 339人阅读 评论(0) 收藏
    先执行linux的clear清屏命令,再执行其他操作 分类: python 小练习 2013-09-13 11:23 441人阅读 评论(0) 收藏
    MySQL 解决ERROR 1045 (28000): Access deniedfor user datam@localhost (using password: YES)的问题 分类: database 2013-09-12 15:52 402人阅读 评论(0) 收藏
    函数名function是一个数据类型,可以赋值 分类: python基础学习 2013-09-12 11:01 366人阅读 评论(0) 收藏
    解决 mysql error: Failed dependencies: 错误 分类: database 2013-09-11 11:23 772人阅读 评论(0) 收藏
    Java之wait()/sleep()和notify()/notifyAll()
    Java之数据流DataInput(Output)Stream 和 字节数组流 ByteArrayInput(Output) Stream的嵌套
    Eclipse的简易教程
    JAVA中的反射机制
  • 原文地址:https://www.cnblogs.com/zp-uestc/p/10304359.html
Copyright © 2020-2023  润新知