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

    科技2024-11-18  21

    错误用法:

    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.csdn.net/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.csdn.net/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.csdn.net/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(""));

     

     

    Processed: 0.010, SQL: 8