Effective Java 第三版——55. 明智而审慎地返回Optional

Tips
书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code
注意,书中的有些代码里方法是基于Java 9 API中的,所以JDK 最好下载 JDK 9以上的版本。

Effective Java, Third Edition

55. 明智而审慎地返回Optional

Java 8之前,编写在特定情况下无法返回任何值的方法时,可以采用两种方法。要么抛出异常,要么返回null(假设返回类型是对象是引用类型)。但这两种方法都不完美。应该为异常条件保留异常(条目 69),并且抛出异常代价很高,因为在创建异常时捕获整个堆栈跟踪。返回null没有这些缺点,但是它有自己的缺陷。如果方法返回null,客户端必须包含特殊情况代码来处理null返回的可能性,除非程序员能够证明null返回是不可能的。如果客户端忽略检查null返回并将null返回值存储在某个数据结构中,那么会在将来的某个时间在与这个问题不相关的代码位置上,抛出NullPointerException异常的可能性。

在Java 8中,还有第三种方法来编写可能无法返回任何值的方法。Optional<T>类表示一个不可变的容器,它可以包含一个非null的T引用,也可以什么都不包含。不包含任何内容的Optional被称为空(empty)。非空的包含值称的Optional被称为存在(present)。Optional的本质上是一个不可变的集合,最多可以容纳一个元素。Optional<T>没有实现Collection<T>接口,但原则上是可以。

在概念上返回T的方法,但在某些情况下可能无法这样做,可以声明为返回一个Optional<T>。这允许该方法返回一个空结果,以表明不能返回有效的结果。返回Optional的方法比抛出异常的方法更灵活、更容易使用,而且比返回null的方法更不容易出错。

在条目 30中,我们展示了根据集合中元素的自然顺序计算集合最大值的方法。

// Returns maximum value in collection - throws exception if empty public static <E extends Comparable<E>> E max(Collection<E> c) { if (c.isEmpty()) throw new IllegalArgumentException("Empty collection"); E result = null; for (E e : c) if (result == null || e.compareTo(result) > 0) result = Objects.requireNonNull(e); return result; }

如果给定集合为空,此方法将抛出IllegalArgumentException异常。我们在条目30中提到,更好的替代方法是返回Optional<E>。下面是修改后的方法:

// Returns maximum value in collection as an Optional<E> public static <E extends Comparable<E>> Optional<E> max(Collection<E> c) { if (c.isEmpty()) return Optional.empty(); E result = null; for (E e : c) if (result == null || e.compareTo(result) > 0) result = Objects.requireNonNull(e); return Optional.of(result); }

如你所见,返回Optional很简单。 你所要做的就是使用适当的静态工厂创建Optional。 在这个程序中,我们使用两个:Optional.empty()返回一个空的Optional,Optional.of(value)返回一个包含给定非null值的Optional。 将null传递给Optional.of(value)是一个编程错误。 如果这样做,该方法通过抛出NullPointerException异常作为回应。 Optional.of(value)方法接受一个可能为null的值,如果传入null则返回一个空的Optional。永远不要通过返回Optional的方法返回一个空值:它破坏Optional设计的初衷。

Stream上的很多终止操作返回Optional。如果我们重写max方法来使用一个Stream,那么Stream的max操作会为我们生成Optional的工作(尽管我们还是传递一个显式的Comparator):

// Returns max val in collection as Optional<E> - uses stream public static <E extends Comparable<E>> Optional<E> max(Collection<E> c) { return c.stream().max(Comparator.naturalOrder()); }

那么,如何选择返回Optional而不是返回null或抛出异常呢?Optional在本质上类似于检查异常(checked exceptions)(条目 71),因为它们迫使API的用户面对可能没有返回任何值的事实。抛出未检查的异常或返回null允许用户忽略这种可能性,从而带来潜在的可怕后果。但是,抛出一个检查异常需要在客户端中添加额外的样板代码。

如果方法返回一个Optional,则客户端可以选择在方法无法返回值时要采取的操作。 可以指定默认值:

// Using an optional to provide a chosen default value String lastWordInLexicon = max(words).orElse("No words...");

或者可以抛出任何适当的异常。注意,我们传递的是异常工厂,而不是实际的异常。这避免了创建异常的开销,除非它真的实际被抛出:

// Using an optional to throw a chosen exception Toy myToy = max(toys).orElseThrow(TemperTantrumException::new);

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zysywd.html