提到著名的 NullPointerException
,相信大家都熟悉。空指针异常不单单经常出现在我们的测试环境的代码里,在生产环境其实也不少见。而为了尽可能的避免空指针异常的出现,我们更是小心翼翼地在代码加各种判断。
Java8 则引入了一个新的类 Optional
,可以有效的避免我们一不小心就写了 NPE
的代码。
先看源码 源码节选,有省略,基于 Java8。(Java9 之后有增强)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 public final class Optional <T > { private static final Optional<?> EMPTY = new Optional<>(); private final T value; private Optional () {this .value = null ;} public static <T> Optional<T> empty () { @SuppressWarnings ("unchecked" ) Optional<T> t = (Optional<T>) EMPTY; return t; } private Optional (T value) { this .value = Objects.requireNonNull(value); } 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); } public T get () { if (value == null ) { throw new NoSuchElementException("No value present" ); } return value; } public boolean isPresent () { return value != null ; } public void ifPresent (Consumer<? super T> consumer) { if (value != null ) consumer.accept(value); } public Optional<T> filter (Predicate<? super T> predicate) { Objects.requireNonNull(predicate); if (!isPresent()) return this ; else return predicate.test(value) ? this : 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));} } 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)); } } 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(); } } }
Optional
源码其实很简单,没有复杂的实现。
Optional 应该怎样用? 创建 Optional 创建有值的 Optional
,可以用以下两种方法:
public static <T> Optional<T> of(T value) public static <T> Optional<T> ofNullable(T value)
of
方法要求传入的 value != null
,所以用的时候要注意,否则还是会产生 NPE
。
ofNullable
传入的值可以为 null,所以当你不确定值到底会不会在某种情况下为 null 的时候,可以使用该方法。
取值 取值的话,我们可以用 get
方法。但问题是,虽然不会导致 NPE
,但是又引入了 NoSuchElementException
。所以如果使用 get
方法也是要通过 isPresent
先判断的。
经过 isPresent
判断的话,还是相当于我们在代码里要主动地加判断。有没有办法可以让我们不加判断呢?有。
public T orElse(T other) public T orElseGet(Supplier<? extends T> other)
这两个方法可以设置一个默认值,如果 value == null
,取出来的就是默认值。
需要注意的是,orElse
传入的是一个对象,也就意味着,如果传的不是 null,那么它就已经经过实例化了,虽然不一定能用到。而 orElseGet
传入的则是一个 Supplier
,而 supplier.get
不一定会执行。比如:
1 2 3 4 5 6 7 8 9 public void getTest () { Optional<String> emptyOpt = Optional.empty(); String emptyDefault = emptyOpt.orElse("emptyStr" ); Optional<String> demoOpt = Optional.of("demo" ); String demoStr = demoOpt.orElseGet(() -> "demoStr" ); System.out.println(emptyDefault); System.out.println(demoStr); }
在上面的例子中,emptyStr
一定会被创建,因此传入的就是创建好的。而 Supplier.get()
并不会被立即执行,因此传入的是一个 Supplier
,只有 !demoOpt.isPresent
才会执行 get
方法。
当然,Optional
提供的还有 map
、filter
、flatmap
,这几个 API 在 Stream 里也有,可以触类旁通。
空值自定义异常 如果对于空值,我们真的想抛一个自定义异常,其实也是支持的。
T orElseThrow(Supplier<? extends X> exceptionSupplier)
1 2 3 4 5 6 7 8 public void exceptionTest () { Optional<String> emptyOpt = Optional.empty(); System.out.println(111 ); String value = emptyOpt.orElseThrow( () -> new RuntimeException("xxx must have a value" ) ); System.out.println(value); }
orElseThrow
给了我们更多自由,让我们不再局限于 NPE
,可以根据业务实际情况来抛异常。
Stream 中用到的 Optional Stream 中也有不少终止操作用到 Optional。
Optional<T> reduce(BinaryOperator<T> accumulator); Optional<T> min(Comparator<? super T> comparator); Optional<T> max(Comparator<? super T> comparator); Optional<T> findFirst(); Optional<T> findAny();
看,Stream 和 Optional,真的很配呢。
总结 Optional 可以有效减少代码可能出现的 NullPointerExceptions,但是不能完全避免,同时还有可能导致 NoSuchElementException。但是总的来说,它真的可以帮我们减少程序错误,可读性也更高。 Optional 可以配合 Stream 一起使用。 谨慎使用 get
方法,它可能会抛异常。 orElse 是直接设置默认值,orElseGet 是提供一个默认值产生的方法,但是不一定会用上。 本质上来讲,Optional 相当于一个容器,里面可以放东西,但不一定放了东西。 注:本文配套代码可在 github
查看:stream-and-lambda