必知必会--Optional那点事

前言

如果你还没有使用过Optional,建议直接从正文开始食用。而对于使用过的Optional同学,可以思考下面四个问题。

1:Optional为什么会出现,解决了什么问题?

2:Optional是为了优化空指针异常,那使用Optional的过程中会不会抛出空指针异常?

3:Optional中Map方法与flatMap方法的区别与联系以及各自的适用场景?

4:Optional的适用场景,是否可以用作对象属性类型吗?方法入参类型?方法返回参数类型?

正文

A container object which may or may not contain a non-null value

Optional作为JDK1.8引入的一个新特性,官方介绍Optional是一个判断对象是否为空的容器。虽然Optional的代码只有350行(还包括大量的注释),但是功能却不简单,算得上小巧而又精悍。在Optional出现之前我们是如何判断为空呢。

if(null == object){...}

相信这段代码大家再熟悉不过了。

假设现在有一个需求,利用下面给出的json串在控制台打印博主的体重。

{
    "1":{
        "name":"bozhu",
        "body":{
            "height":180,
            "weight":78
        },
        "marriage":"single"
    }
}

我可以这么写

版本一

String weight = JSONObject.parseObject(json).getJSONObject("1")
    .getJSONObject("body")
    .getString("weight");

但是大家会发现这段代码极易报错,因为你并不确定json串中一定会包含body或者weight信息。每一次调用JSONObject#get方法都有可以抛出空指针异常。既然如此,我们需要在每调用一次JSONObject#get方法之后进行判空,修改后的代码是这样的。

版本二

JSONObject bozhuJson = JSONObject.parseObject(json).getJSONObject("1");
if(null!= bozhuJson) {
    JSONObject bodyJson = bozhuJson.getJSONObject("body");
    if (null != bodyJson) {
        String weight = bodyJson.getString("weight");
    }
}

当你看到这段代码你一定会觉得很糟糕,因为当对象的属性层级愈来越深,你需要写更多的判空代码。这部分工作无聊且枯燥,而且一旦因为某个判空逻辑漏写,根据墨菲定律,在未来某个时间这段疏忽的代码一定会报错。尽管你可以在上线前进行充分的测试,你没法保证测试可以覆盖到每处细节,而且这也很浪费资源。

这时候Optional就像一束光出现了,利用Optional修改后的代码是这样的。

版本三

String weight = Optional.ofNullable(JSONObject.parseObject(json))
    .map(jsonObject -> JSONObject.parseObject(jsonObject.getString("1")))
    .filter(jsonObject -> "bozhu".equals(jsonObject.get("name")))
    .map(jsonObject -> JSONObject.parseObject(jsonObject.getString("body")))
    .map(jsonObject -> jsonObject.getString("weight"))
    .orElse("70");

我们可以发现版本三的代码和版本一的代码有一点点像,但是不同之处在于利用了Optional容器包装了Json对象。在Optional的相应方法的内部都进行了判空校验,如果Optional容器的对象不为空,会执行方法传入的Lambda表达式,否则不执行。

以Optional#map方法为例。

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    //1:判断mapper不为空
    Objects.requireNonNull(mapper);
    //2:判断Optional容器中的对象不为空
    if (!isPresent())
        return empty();
    else {
        //3:如果Optional容器中对象不为空,执行mapper。
        //利用mapper的执行结果生成一个Optional对象。
        return Optional.ofNullable(mapper.apply(value));
    }
}

在Optional中还存在一个flatMap()方法,与Optional#map不同之处在于接收的参数mapper必须是一个Optional对象。Optional#flatMap与map方法都会返回一个Optional对象,但是每次调用map方法都会生成一个新的Optional对象,而flatMap方法会返回传入参数中被重新赋值的Optional对象。

还有一个不同点在于对于map方法而言当传入的mapper对象为空时,抛出NullPointerException异常。而对于flatMap方法而言,mapper对象为空或者mapper对象的执行结果为空时,抛出NullPointerException异常。

/**
 *This method is similar to {@link #map(Function)},
 * but the provided mapper is one whose result is already an {@code Optional},
 * and if invoked, {@code flatMap} does not wrap it with an additional
 * {@code Optional}.
 */
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
    //1:判断mapper不为空
    Objects.requireNonNull(mapper);
    //2:判断Optional容器中的对象不为空
    if (!isPresent())
        return empty();
    else {
        //3:当Optional容器中对象不为空,执行mapper的函数代码。
        //如果mapper执行结果不为空将其返回,一个重新赋值的Optional对象。
        return Objects.requireNonNull(mapper.apply(value));
    }
}

值得注意的是Optional没有实现Serializable接口,因此Optional不可以用作对象属性的类型。同时不推荐用作函数的参数类型,但是可以用作函数的返回类型。

Optional的代码逻辑十分简单,因此不做额外的阐述。如果有问题可以在评论区询问。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值