💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# Java 8 `Optional`:完整参考 > 原文: [https://howtodoinjava.com/java8/java-8-Optional-complete-reference/](https://howtodoinjava.com/java8/java-8-Optional-complete-reference/) 我们所有人都必须在我们的应用程序中遇到`NullPointerException`。 当您尝试利用尚未初始化的对象引用,使用`null`初始化或根本不指向任何实例的对象引用时,会发生此异常。***`NULL`仅表示“不存在值”***。 罗马人很可能只是一个人,没有遇到这个空缺的问题,而是从 I,II,III 开始计数(不为零)。 也许,他们无法模拟市场上苹果的缺乏。:-) “我称之为我十亿美元的错误。” – [**C. A. R. Hoare Hoare**](https://en.wikipedia.org/wiki/C._A._R._Hoare "C._A._R._Hoare"),他发明了空引用 在本文中,我将针对该特定用例(即[`Optional`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html "Optional"))讨论 [**java 8 特性之一**](//howtodoinjava.com/category/java8/ "java 8")。 为了清楚和区分多个概念,本文分为多个部分。 ```java Discussion Points 1) What is the Type of Null? 2) What is wrong with just returning null? 3) How Java 8 Optional provide the solution? a) Creating Optional objects b) Do something if a value is present c) Default/Absent values and actions d) Rejecting certain values Using the filter method 4) What is inside Optional make it work? 5) What is Optional trying to solve? 6) What is Optional not trying to solve? 7) How should Optional be used? 8) Conclusion ``` ## 1)什么是空值? 在 Java 中,我们使用引用类型来访问对象,而当我们没有特定的对象作为引用点时,则将此类引用设置为`null`表示没有值。是吧? `null`的使用是如此普遍,以至于我们很少对此多加思考。 例如,对象的字段成员会自动初始化为`null`,而程序员通常会在没有初始值的引用类型时将引用类型初始化为`null`,并且通常在每次我们不知道或不知道的时候都使用`null`没有参考价值。 仅供参考,在 Java 中`null`实际上是一种类型,是一种特殊的类型。 它没有名称,因此我们不能声明其类型的变量或将任何变量强制转换为它。 实际上,只有一个可以与之关联的值(即字面值为`null`)。 请记住,与 Java 中的任何其他类型不同,可以将空引用安全地分配给任何其他引用类型,而不会出现任何错误(见 [**JLS 3.10.7**](https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.10.7 "jls 3.10.7") 和 [**4.1**](https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.1 "jls 4.1"))。 ## 2)仅返回`null`有什么问题? 通常,API 设计人员将描述性的 Java 文档放入 API 中,并在其中提到 API 可以返回空值,在这种情况下,返回值可以是`null`。 现在,问题在于 API 的调用者可能由于任何原因而错过了读取 Javadoc 的操作,并且**忘记了处理空值**的情况。 肯定的将来这将是一个错误。 并且相信我,这经常发生,并且是空指针异常的主要原因之一,尽管不是唯一的原因。 因此,在这里上一课,当您第一次使用它时,请务必(至少)阅读它的 Javadocs。 > 现在我们知道在大多数情况下`null`是一个问题,什么是最好的处理方式? 一个好的解决方案是始终**始终使用某个值**初始化您的对象引用,而永远不要使用`null`进行初始化。 这样,您将永远不会遇到`NullPointerException`。 很公平。 但实际上,我们始终没有默认值作为参考。 那么,这些案件应该如何处理? 上述问题在许多方面都是正确的。 好吧, **java 8 `Optional`** 是这里的答案。 ## 3)Java 8 `Optional`如何提供解决方案? 可选的是用非空值替换可空`T`引用的方法。 `Optional`可以包含非空`T`引用(在这种情况下,我们称该引用为“存在”),也可以不包含任何内容(在这种情况下,我们称该引用为“不存在”)。 > 请记住,**从未说过`Optional`“包含`null`”**。 ```java Optional<Integer> canBeEmpty1 = Optional.of(5); canBeEmpty1.isPresent(); // returns true canBeEmpty1.get(); // returns 5 Optional<Integer> canBeEmpty2 = Optional.empty(); canBeEmpty2.isPresent(); // returns false ``` 您也可以**将`Optional`看做为包含或不包含值**的单值容器。 重要的是要注意,`Optional`类的目的不是替换每个单个空引用。 相反,它的目的是**帮助设计更易于理解的 API** ,以便仅通过读取方法的签名即可知道是否可以期望一个可选值。 这迫使您从`Optional`中获取值并对其进行处理,同时处理`Optional`为空的情况。 好吧,这正是空引用/返回值的解决方案,最终导致`NullPointerException`。 以下是一些示例,以了解有关应如何在应用程序代码中创建和使用`Optional`的更多信息。 ## a)创建`Optional`对象 创建`Optional`的主要方法有 3 种。 i)使用`Optional.empty()`创建空的`Optional`。 ```java Optional<Integer> possible = Optional.empty(); ``` ii)使用`Optional.of()`创建具有默认非空值的`Optional`。 如果在`of()`中传递`null`,则立即引发`NullPointerException`。 ```java Optional<Integer> possible = Optional.of(5); ``` iii)使用`Optional.ofNullable()`创建一个可能包含空值的`Optional`对象。 如果参数为`null`,则生成的`Optional`对象将为空(请记住,该值不存在;请勿将其读取为`null`)。 ```java Optional<Integer> possible = Optional.ofNullable(null); //or Optional<Integer> possible = Optional.ofNullable(5); ``` ## b)如果存在`Optional`值,则执行某些操作 第一步是获取`Optional`对象。 现在,在检查其内部是否包含任何值之后,再使用它。 ```java Optional<Integer> possible = Optional.of(5); possible.ifPresent(System.out::println); ``` 您也可以将以下代码覆盖为上面的代码。 但是,不建议您使用`Optional`,因为与嵌套的空检查相比,它没有太大的改进。 它们看起来确实完全相似。 ```java if(possible.isPresent()){ System.out.println(possible.get()); } ``` 如果`Optional`对象为空,则不会打印任何内容。 ## c)默认/不存在的值和动作 如果确定操作结果为空,则编程中的典型模式是返回默认值。 通常,您可以使用三元运算符。 但是使用`Optional`,您可以编写如下代码: ```java //Assume this value has returned from a method Optional<Company> companyOptional = Optional.empty(); //Now check optional; if value is present then return it, //else create a new Company object and retur it Company company = companyOptional.orElse(new Company()); //OR you can throw an exception as well Company company = companyOptional.orElseThrow(IllegalStateException::new); ``` ## d)使用过滤方法拒绝某些值 通常,您需要在对象上调用方法并检查某些属性。 例如在下面的示例代码中,检查公司是否设有“财务”部门; 如果有,请打印出来。 ```java Optional<Company> companyOptional = Optional.empty(); companyOptional.filter(department -> "Finance".equals(department.getName()) .ifPresent(() -> System.out.println("Finance is present")); ``` [**过滤方法**](//howtodoinjava.com/java8/java-8-tutorial-streams-by-examples/ "java 8 streams")以[**谓词**](//howtodoinjava.com/2014/04/04/how-to-use-predicate-in-java-8/ "predicate")为参数。 如果`Optional`对象中存在一个值,并且该值与谓词匹配,则过滤方法将返回该值;否则,该方法将返回该值。 否则,它返回一个空的`Optional`对象。 如果在[`Stream`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html "Stream")接口中使用了过滤方法,则可能已经看到了类似的模式。 很好,这段代码看起来更接近问题语句,并且没有妨碍我们进行冗长的`null`检查! 哇!! 从编写痛苦的嵌套`null`检查到编写可编写,可读且可更好地免受`null`指针异常保护的声明性代码,我们已经走了很长一段路。 ## 4)`Optional`内部有什么使它起作用? 如果打开`Optional.java`的源代码,则会发现`Optional`持有的值定义为: ```java /** * If non-null, the value; if null, indicates no value is present */ private final T value; ``` 并且,如果您定义一个空的`Optional`,则它声明如下。 **静态关键字可确保每个 VM** 通常仅存在一个空实例。 ```java /** * Common instance for {@code empty()}. */ private static final Optional<?> EMPTY = new Optional<>(); ``` 默认的**无参构造器定义为私有** ,因此您无法创建`Optional`的实例,但上述三种方法除外。 当您创建一个`Optional`时,最终会发生以下调用,并将传递的值分配给“`value`”属性。 ```java this.value = Objects.requireNonNull(value); ``` 当您尝试从`Optional`获取值时,如果没有抛出`NoSuchElementException`,则会获取该值: ```java public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; } ``` 同样,在`Optional`类中定义的其他函数仅在“`value`”属性附近起作用。 浏览`Optional.java`的[**源代码**](http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/Optional.java "Optional.java")以获取更多信息。 ## 5)`Optional`试图解决什么? `Optional`尝试通过考虑到有时缺少返回值来增加构建更具表现力的 API 的可能性,从而减少 Java 系统中空指针异常的数量。 如果从一开始就存在`Optional`,那么今天大多数库和应用程序可能会更好地处理缺少的返回值,从而减少了空指针异常的数量以及总体上的错误总数。 通过使用`Optional`,用户不得不考虑特殊情况。 除了因提供空名而提高了可读性之外,`Optional`的**最大优点是其防白痴**。 如果您要完全编译程序,它会迫使您积极考虑不存在的情况,因为您必须主动展开`Optional`并解决该失败情况。 ## 6)`Optional`不尝试解决的问题是什么? `Optional`**并不是要避免所有类型的空指针**的机制。 例如方法和构造器的强制输入参数仍然必须进行测试。 就像使用`null`时一样,`Optional`不能帮助传达缺失值的含义。 因此,该方法的调用者仍然必须检查 API 的 javadoc,以了解缺少`Optional`的含义,以便对其进行正确处理。 请注意,`Optional`不能在以下情况下使用,因为它可能不会给我们带来任何好处: * 在域模型层(不可序列化) * 在 DTO 中(不可序列化) * 在方法的输入参数中 * 在构造器参数中 ## 7)应该如何使用`Optional`? **几乎在所有时间**都应该使用`Optional`**作为可能不返回值的函数**的返回类型。 这是来自 OpenJDK 邮件列表的引言: > JSR-335 EG 相当强烈地认为`Optional`不应仅用于支持 option-return 惯例。 > > 有人建议甚至将其重命名为“`OptionalReturn`”。 从本质上讲,这意味着`Optional`仅应在确实达到目的的情况下用作某些服务,存储库或工具方法的返回类型。 ## 8)总结 在本文中,我们了解了如何采用新的 **Java SE 8 `java.util.Optional`**。`Optional`的目的不是替换代码库中的每个单个`null`引用,而是帮助您设计更好的 API,其中,仅通过读取方法的签名,用户就可以知道是否期待并妥善处理`Optional`值。 仅此特性就可以了。 在评论部分让我知道您的想法。 **祝您学习愉快!**