Java 8 Optional 续1

前面讲过Java中Null表示空对象时带来NullPointerException的问题,以及解决这些问题的几种办法。其中之一就是使用Optional,今天我们要来了解更多的Optional的使用方法。

从下面这段简单的代码开始:

    public String getName() {
        Company company = getCompany();
        if (company != null) {
            if (company.founder != null) {
                return company.founder.name;
            } else
                return null;
        } else
            return null;
    }
代码的意图是从一个Company实例中取出founder的name。 这样简单的逻辑,却用 If 嵌套来防止Null带来的NullPointerException,显然啰嗦,但似乎也无可奈何了。当然你会改写一下程序来减少代码行数:

    public String getName() {
        Company company = getCompany();
        return company != null ? company.founder != null ? company.founder.name : null : null;
    }
上面的代码看起来已经没有什么可以挑剔的了,但是我们会发现一半的逻辑在做与方法无关Null的判断,而真正的逻辑却被淹没在这些副作用当中。我们现在就用Optional的一些技巧来移除Null判断,让程序只关心真正的逻辑。 

  1. 步骤1: 找出可空(nullable)对象(例如company),用Optional.empty() 来代替Null, Optional.of(T) 来代替非空对象,这样我们处理的对象就不可能为Null了。
  2. 步骤2: 提供一种用来访问可空对象的操作,如果可空对象为empty,直接返回empty。如果不为空,则执行操作,返回Optional.ofNullable(o)。
  3. 步骤3: 重复步骤2,直至得到最终结果。

所以,首先要让getCompany()方法返回一个Optional<Company> company, 然后根据步骤2,给company绑定一个提取founder的操作,并且把founder包装成一个Optional<Founder>,为此我们需要一个绑定操作到某个可空对象的方法:

public class Optionals {
    static <V, R> Optional<R> bind(Optional<V> optional, Function<V, R> function) {
        if (optional.isPresent())
            return Optional.ofNullable(function.apply(optional.get()));
        else
            return Optional.empty();
    }
}

上面的方法就是绑定一个function到一个可空的对象,如果可空对象存在,则调用function,否则就直接返回空。所以,在取name的过程中,company或者founder为Optional.empty(), 我们也仍然可以绑定function到它们,而无须判断。这些判断完全交给了bind来做,我们只需要关心萃取的逻辑,想在代码就可以编程这样:

Optional<String> name= bind(bind(getCompany(), c -> c.founder), f -> f.name);

怎么样代码看起更少了,也彻底甩掉了Null这个累赘。在看看这个递归调用,很熟悉不是吗?多像Scheme中写Labmda 表达式!这是纯函数式编程的风格,但对于Java来说,我们希望这个绑定function的操作能在放在对象上来,而不需要借助Optionals.bind这样一个第三者。所以我们想办法给我们Optional<V> 对象添加绑定操作的功能,这个简单,我们很容易想到各种增加功能的设计模式:你可以直接继承Optional<V>或者用一个新的类型来装饰Optional<V>, 这里我们就用Decorator 模式来实现:

public class StreamOptional<V> extends AbstractStreamOptional<V> {

    public StreamOptional(Optional<V> optional) {
        super(optional);
    }

    static <R> StreamOptional<R> from(Optional<R> optional) {
        return new StreamOptional(optional);
    }

    public <R> StreamOptional<R> bind(Function<V, R> function) {
        if (this.isPresent()) {
            return StreamOptional.from(Optional.<R>ofNullable(function.apply(this.get())));
        } else
            return StreamOptional.from(Optional.<R>empty());
    }
然后我们再来实现上面例子,代码就变成这样:
StreamOptional<String> name = from(getCompany()).bind(company -> company.founder).bind(founder -> founder.name);


其中From函数用来包装Optional成StreamOptional, StreamOptional的bind返回另一个StreamOptional,这样就可以像流一样按照顺序操作它们。和之前Optionals.bind的递归调用不同,这种链式调用过程不会积累调用栈,而且同样无须担心Null的问题。

总结: 在对可空对象的处理时,为避免做无关的事情,绑定操作到这个对象上,这种做法其实来源了Monad模型,在一个数据上赋予一系列的计算过程的,可以像管道一样处理数据。无论是函数风格还是链式调用,都体现到了这一点。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值