Optional<T>到底要怎么用?从源码分析下是否真的是用来解决空指针异常的类

本文探讨了Java Optional类的误用和正确使用方式。常见错误包括使用isPresent后再get(),作为方法参数和成员属性。正确用法在于作为方法返回值类型,以有选择性地处理可能为空的对象。Optional提供的方法如ifPresent、filter、map、orElse等提供了处理空值的优雅方式,避免直接抛出NullPointerException。

错误用法:

1、先使用isPresent来判断是否有值,然后使用get()方法取出元素。

Optional<String> optional = Optional.empty();
if (optional.isPresent()) {
    String str = optional.get();
}

public boolean isPresent() {
    return value != null;
}

说明:我直接用!Objects.isNull()判断下,再处理它不香吗?为什么还要用这个Optional呢?

2、使用Optional作为方法参数来使用

void badMethod(Optional<String> optional) {
    ...;
}

说明:每个方法都应该对参数进行合法性校验的,除非是内部使用的private方法,我们确定参数的取值情况时可以不进行校验。这个方法的参数意思就是说:我会判断是否为空的(用的错误方法1),你们放心传给我参数就好了。这不是废话?

3、使用Optional来作为类的成员属性

public class BadClass {
    private Optional<String> optional;
    ...
}

说明: 类的成员属性,对象类型的默认值本来就是null的,你搞一个Optional来当成员属性是几个意思??难道是:“大家好!我这个类的属性是可能为null的,我考虑很周到的,我代码逼格很高的”。我表示很无语,因为真的有“前辈”这么用的!

 

 正确用法:

定义方法的返回值的类型为Optional,因为方法的返回值可能为空,也可能有值的。根据返回值的情况,有选择性的处理相应的逻辑。这里我把“有选择性的”标记了,因为这个就是Optional的中文意思,也是它的用法所代表的意思。

 

两个成员属性:封装的值、空Optional对象

/*空Optional对象*/
private static final Optional<?> EMPTY = new Optional<>();

/*封装的值*/
private final T value;

empty方法的使用:返回的就是静态的成员属性EMPTY

    public static<T> Optional<T> empty() {
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

先看下创建Optional对象的两个方式:(这个不是重点)

1、Optional.of():

    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

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

    public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

说明:可以看到如果传一个null给of方法的话,会直接抛一个空指针异常,还有一个是optional.get()方法,如果value为空的话,也是直接抛异常的。说明人家作者根本不是说要用Optional来防止NullPointerException异常的发生的,而是用一种有选择性的简洁的方式去处理一个可能为空的对象,不知道怎么就被误认为了用Optional就没有空指针异常的问题了。

2、Optional.ofNullable():

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

这个方法是可以传入null值的,不会抛出空指针异常。

 

下面围绕这个“有选择性的”展开介绍: 

 1、针对一个可能为空的对象,如果有值的话就选择处理这个值使用ifPresent(Consumer)

    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

 说明:如果value值不为空的话,消费这个值(也就是处理这个值)

 用法:

Optional<String> optional = Optional.of("river66");
optional.ifPresent(System.out::println);

关于Consumer的使用,可以参考下:https://blog.youkuaiyun.com/river66/article/details/108178965

 2、针对一个可能为空的对象,如果不符合给定的条件,则作为空处理,使用filter(Predicate)

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

 关于Predicate的用法,可以参考下:https://blog.youkuaiyun.com/river66/article/details/107028467

 用法:

Optional<String> optional = Optional.of("");
optional = optional.filter(""::equals);

3、针对一个可能为空的对象,只要不是null值,就转化为另一个值,使用map(Function)

    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));
        }
    }

 用法:

        Optional<String> optional = Optional.of("123");
        Optional<Integer> op = optional.filter((a)->{
            boolean b;
            try {
                Integer.parseInt(a);
                b = true;
            } catch (NumberFormatException e) {
                b = false;
            }
            return b;
        }).map(Integer::parseInt);

说明:如果filter中返回的是false,则意味着value将为null,但是map中调用的是ofNullable,所以map是可以跟在filter后使用的。而flatMap这个方式是不可以跟在filter后面的,因为会抛出NullPointerException。

 关于Function的用法,可以参考下:https://blog.youkuaiyun.com/river66/article/details/108545027

 4、针对一个可能为空的对象,不为空则返回value,为空则返回给定的默认值,使用T orElse(T other)

public T orElse(T other) {
    return value != null ? value : other;
}

 用法:

Optional<String> optional = Optional.empty();
String notNull = optional.orElse("");

5、针对一个可能为空的对象,不为空则返回value,为空则返回给定的默认值(Lambda表达式返回的值,即Supplier),使用orElseGet(Supplier)。

    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }

 用法:

Optional<String> optional = Optional.empty();
String notNull = optional.orElseGet(()->{return "";});

 6、针对一个可能为空的对象,不为空则返回value,为空就抛异常,使用orElseThrow(Supplier) throws X

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

用法:

Optional<String> optional = Optional.empty();
String a = optional.orElseThrow(() -> new NoSuchElementException(""));

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值