• 2013-08-22

    你应该更新的Java知识之Optional高级用法

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/235334714.html

    你应该更新的Java知识之常用程序库(一)
    你应该更新的Java知识之常用程序库(二)
    你应该更新的Java知识之构建工具
    你应该更新的Java知识之Observer
    你应该更新的Java知识之集合初始化
    你应该更新的Java知识之集合操作
    你应该更新的Java知识之惰性求值
    你应该更新的Java知识之Optional

    介绍了Optinal的基本用法,我们来看一个有趣的例子,找到一个人的出生国家。按照传统的思路,代码大约是这个样子:

    Place place = person.getPlaceOfBirth();
    if (place != null) {
     City city = place.getCity();
     if (city != null) {
       Province province = city.getProvince();
       if (province != null) {
         return province.getCountry();
       }
     }
    }

    return null;

    如果你对整洁代码稍有追求,这样的if套if都会让你觉得不爽。让我们尝试用Optional改造它,不过,事先声明一下,以下代码并不在Guava代码库里,而是自行的扩展,也是弥补Guava Optional的缺失,你可以把下面的代码添加到你自己的程序库中,作为基础代码:

    首先,我们要给Optionals添加一个方法:

    public class Optionals {
       public static <T, U> Optional bind(Optional value,
                                           Function<T, Optional> function) {
         if (value.isPresent()) {
          return function.apply(value.get());
        }

        return absent();
      }
    }
    (参见具体代码

    这个方法的意图是,对一个Optional值(value)执行一个操作(function),如果value不是空,则对value执行操作,否则,返回空。

    如果单纯从这个函数,你还不是很清楚它到底能做些什么,那我们就直接来看代码:

    bind(
     bind(
       bind(
         bind(personOptional, getPlaceOfBirth()),                          
         getCityFromPlace()),
       getProvinceFromCity()),
     getCountryFromProvince());

    我们连着用了几个bind连Person对象里一层一层地往外找我们所需的值,如你所料,这里的每个函数实际上都是一个函数对象,我们就以其中的一个演示一下基本的做法:

    Function<Province, Optional> getCountryFromProvince() {
     return new Function<Province, Optional>() {
       @Override
       public Optional apply(Province input) {
         return fromNullable(input.getCountry());
       }
     };
    }

    把所有这些放在一起你就应该理解了,在这中间执行的任何一个环节如果出现空值,那么整个的返回值就是一个空值,否则,它就会一层一层的执行下去。

    这样一来,如果我们把bind函数视为程序库里的函数,那我们的客户端代码里面,一个if都没有出现,我们成功地消除那一大堆的if嵌套。

    不过,这种括号套括号的用法颇有Lisp风味,作为一个Java程序员,我们对于这样的写法还着实需要适应一下。让我们再进一步探索一下,看看怎么能把它做得更Java一些。

    public class FluentOptional {
       private Optional optional;

       private FluentOptional(Optional optional) {
         this.optional = optional;
      }

      public static FluentOptional from(Optional optional) {
         return new FluentOptional(optional);
     }

      public FluentOptional bind(Function<T, Optional> function) {
         if (isPresent()) {
             return from(function.apply(get()));
         }

          return from(Optional.absent());
     }

      ...

    (参见具体代码

    通过这段代码,我们可以用FluentOptional提供一个对Optional类的封装,这里面我们新增了两个方法from和bind,其它方法都是可以由Optional提供,实现很容易,这里省略了。我们看看通过这个新实现,我们的方法变成了什么模样:

    from(personOptional)
      .bind(getPlace())
      .bind(getCityFromPlace())
      .bind(getProvinceFromCity())
      .bind(getCountryFromProvince());

    怎么样,如此一来,代码就就像Java代码了吧!

    实际上,这种做法也是来自一种函数式编程的理念:Maybe Monad,这是Haskell程序设计语言为探索纯函数式编程所做的努力之一,这里就不做过多的介绍了。

    分享到:

    历史上的今天:

    学无止境 2005-08-22
    引用地址: