【Java】Optional类 使用 及 实现

为什么使用Optional类?还是一段出现了很多次的代码,我们定义一个Response类用于描述广告接口的返回,内部包含一个广告Ad,Ad内部包含素材信息Material,素材内部包含链接信息。类定义如下:

@Data
class Ad{
    Material material;
    // other
}

@Data
class Material{
    String url;
    // other
}

@Data
class Response{
    int code;
    Ad ad;
}

由于出现了对象的层级引用,使得我们想要获取最里面的属性时,必须经过多层取属性操作,只要有一处为空,就会报NPE,所以这能写成下面的形式:

        if (response != null) {
            if (response.ad != null) {
                if (response.ad.material != null) {
                    System.out.println(response.ad.material.url);
                }
            }
        }

当然这里正常要使用get方法而不是直接点号取属性,这里只是一个例子。这样写本身没有任何问题,代码也很健壮,只是可读性比较差,取一个属性,竟让需要如此之多的判空语句。所以,java8引入了Optional类来解决这个问题。

看下Optional类如何完成这个功能:

        String s = Optional.ofNullable(response)
                .map(Response::getAd)
                .map(Ad::getMaterial)
                .map(Material::getUrl)
                .orElse("unknown");
        System.out.println(s);

看上去是不是好了很多?

用法:

简单说,Optional的思路就是提供一个容器类或者包装类打包我们的对象,然后把判空逻辑封装在Optional类内部,再提供判空后的api来供我们取值。所以,如果我们想要取一个对象的属性,要先使用Optional的两个静态方法把对象包起来。
of和ofNullable方法的区别在于,如果对象是null,of方法会报错。而ofNullable方法则会返回空的Optional,至于这个空的Optional是什么,会在后面的实现部分解释。通常我们需要使用的是ofNullable方法。


然后我们可以使用Optional的map或者flatMap方法取值。这里需要注意,stream类中也有相同名字的方法,但是二者并没有关系。在Optional类中,这两个方法用于映射值,区别在于map方法会把映射以后的值打包在新的Optional实例中,而flatMap不会打包,但是要求我们的mapper映射函数返回值为打包以后的Optional实例。换句话说map函数的mapper函数类似e->e.getXXX(),而flatMap函数的mapper函数类似e->Optional.ofNullable(e.getXXX()),flatMap函数存在的意义在于,可能我们会在mapper函数中实现打包,这时map时我们就不在需要打包。通常,我们只需要使用map函数即可。

map的例子在上面已经有了,下面是flatMap的例子:

        String s1 = Optional.ofNullable(response)
                .flatMap(e->Optional.ofNullable(e.getAd()))
                .flatMap(e->Optional.ofNullable(e.getMaterial()))
                .flatMap(e->Optional.ofNullable(e.getUrl()))
                .orElse("unknown");
        System.out.println(s1);

最终取值时,Optional提供了3个方法。当检测Optional里的值为空时,orElse返回一个指定的默认值,orElseGet允许我们提供一个Supplier接口来生成默认值,orElseThrow跑出指定的异常。

        String s = Optional.ofNullable(response)
                .map(Response::getAd)
                .map(Ad::getMaterial)
                .map(Material::getUrl)
                .orElseGet(()->"unknown" + Math.random());
        System.out.println(s);
        String s = Optional.ofNullable(response)
                .map(Response::getAd)
                .map(Ad::getMaterial)
                .map(Material::getUrl)
                .orElseThrow(NullPointerException::new);
        System.out.println(s);

最后,Optional还为我们提供了过滤功能,filter函数允许我们提供一个Predict接口,用于过滤对象的值,如果对象为空或判定为false,返回空的Optional,否则返回原来的Optional。

        String raw = "abced";
        String r = Optional.ofNullable(raw).filter(e->e.length() < 3).orElse("INVALID");
        System.out.println(r);

至此,关于如何使用Optional避免嵌套的判空已经介绍完,下面看下Optional类的实现。

实现:

Optional类是一个泛型容器类。内部只有一个实例变量:

private final T value;

该变量存放被包装的对象。
除此之外,还有一个静态变量:

private static final Optional<?> EMPTY = new Optional<>();

如果被包装的变量为null,就会返回这个静态变量。下面看下构造函数:

    private Optional() {
        this.value = null;
    }
    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

无参形式用于构造被打包对象为null的Optional实例,之前的静态变量EMPTY,就是调用该函数构造的。含参构造函数用于构造被打包对象不为null的Optional实例。
注意构造函数为私有的,所以只能使用下面的静态方法来构造Optional打包实例:

    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

of如果接受null参数会报错,ofNullable如果接受null参数会返回EMPTY实例,即空的打包实例。

    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }

这里的map方法,先检测被打包对象是否为null,如果是就返回EMPTY,否则就调用mapper映射函数,最终再封装成Optional实例返回。

    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

而flatMap则不会将最终结果打包成Optional实例。

下面是filter方法:

    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

如果被打包对象为null或者没有通过predicate检测,就会返回EMPTY,否则返回原Optional对象。

最后是orElse,orElseGet,orElseThrow:

    public T orElse(T other) {
        return value != null ? value : other;
    }
    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }
    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值