原创

JAVA8系列教程-Optionals

温馨提示:
本文最后更新于 2020年04月16日,已超过 964 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

我们所有人都必须NullPointerException在我们的应用程序中遇到过。当您尝试利用尚未初始化的对象引用,使用null初始化或根本不指向任何实例的对象引用时,会发生此异常。NULL仅表示“不存在值”罗马人很可能只是一个人,没有遇到这个空洞的问题,而是从I,II,III开始计数(不为零)。也许,他们无法模拟市场上苹果的缺乏。[:-)]

“我称之为我十亿美元的错误。” – CAR Hoare爵士,关于他的无效参考文献的发明

在本文中,我将讨论此特定用例Java 8功能之一,Optional为了清楚和区分多个概念,本文分为多个部分。

讨论要点

1)什么是空值?
2)仅返回null有什么问题?
3)Java 8 Optionals如何提供解决方案?
a)创建可选对象
b)如果存在
值,则执行
某些
操作c)默认值/ 不存在值和操作d)拒绝某些值使用过滤器方法4)可选里面的东西使其起作用?
5)可选试图解决什么?
6)Optional不尝试解决的问题是什么?
7)应该如何使用可选的?
8)结论

1)什么是空值?

在Java中,我们使用引用类型来访问对象,并且当我们没有特定的对象来指向我们的引用时,则将此类引用设置为null表示不存在值。对?

null的使用是如此普遍,以至于我们很少对此多加思考。例如,对象的字段成员会自动初始化为null,程序员通常在没有初始值可给它们的情况下将引用类型初始化为null,并且通常,每次我们不知道或不知道时,都会使用null没有参考价值。

仅供参考,在Java中null实际上是一种类型,是一种特殊的类型它没有名称,因此我们无法声明其类型的变量或将任何变量强制转换为它;实际上,只有一个可以与之关联的值(即,文字为null)。请记住,与Java中的任何其他类型不同,可以将空引用安全地分配给任何其他引用类型,而不会出现任何错误(请参见JLS 3.10.74.1)。

2)仅返回null有什么问题?

通常,API设计人员将描述性的Java文档放入API中,并在其中提到API可以返回空值,在这种情况下可以返回null。现在,问题在于API的调用者可能由于某种原因而错过了阅读javadoc的时间,而忘记了处理null的情况将来肯定会是一个错误。

并且相信我,这经常发生,并且是空指针异常的主要原因之一,尽管不是唯一的原因。因此,在这里上一课,当您第一次使用它时,请务必先阅读它的Java dcos(…至少是[[-]])。

现在我们知道在大多数情况下null是一个问题,什么是最好的处理方式?

一个好的解决方案是始终使用一些value初始化对象引用,而永远不要使用null 初始化这样,您将永远不会遇到NullPointerException很公平。但实际上,我们始终没有默认值作为参考。那么,这些案件应该如何处理?

上述问题在许多方面都是正确的。好吧,Java 8Optionals是这里的答案。

3)Java 8 Optionals如何提供解决方案?

可选的是用非空值替换可空T引用的方法。Optional可以包含非空T引用(在这种情况下,我们称该引用为“存在”),也可以不包含任何内容(在这种情况下,我们称该引用为“不存在”)。

请记住,从来没有说过可选的“ contain null”

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的主要方法有3种。

i)使用Optional.empty()创建空的可选。

Optional<Integer> possible = Optional.empty();

ii)使用Optional.of()创建具有默认非空值的可选项。如果在of()中传递null,则立即引发NullPointerException。

Optional<Integer> possible = Optional.of(5);

iii)使用Optional.ofNullable()创建一个可选对象,该对象可以包含空值。如果parameter为null,则生成的Optional对象将为空(请记住,该值不存在;请勿将其读取为null)。

Optional<Integer> possible = Optional.ofNullable(null);
//or
Optional<Integer> possible = Optional.ofNullable(5);

b)如果存在可选值,则执行某些操作

您得到的Optional对象是第一步。现在让我们在检查其内部是否包含任何值之后使用它。

Optional<Integer> possible = Optional.of(5);
possible.ifPresent(System.out::println);

您也可以将以下代码重写为上面的代码。但是,这不是推荐的用法,Optional因为它与嵌套null检查相比没有太大的改进。它们看起来确实完全相似。

if(possible.isPresent()){
    System.out.println(possible.get());
}

如果Optional对象为空,则不会打印任何内容。

c)默认/不存在的值和动作

如果确定操作结果为空,则编程中的典型模式是返回默认值。通常,您可以使用三元运算符。但是使用Optionals,您可以编写如下代码:

//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)使用过滤方法拒绝某些值

通常,您需要在对象上调用方法并检查一些属性。例如,在下面的示例代码中,检查公司是否设有“财务”部门;如果有,请打印出来。

Optional<Company> companyOptional = Optional.empty();
companyOptional.filter(department -> "Finance".equals(department.getName())
                    .ifPresent(() -> System.out.println("Finance is present"));

所述过滤器的方法需要一个谓词作为参数。如果Optional对象中存在一个值并且该值与谓词匹配,则filter方法将返回该值;否则,该方法将返回该值。否则,它返回一个空Optional对象。如果在Stream接口上使用了filter方法,则可能已经看到了类似的模式

很好,这段代码看起来更接近问题陈述,并且没有妨碍我们进行冗长的null检查!

哇!!从编写痛苦的嵌套null检查到编写可组合,可读且可更好地免受null指针异常保护的声明性代码,我们已经走了很长一段路。

4)Optional内部有什么使它起作用?

如果打开Optional.java的源代码,则会发现Optional持有的值定义为:

/**
 * If non-null, the value; if null, indicates no value is present
 */
private final T value;

并且,如果您定义一个空的Optional,则它声明如下。static关键字可确保每个VM通常仅存在一个空实例

/**
 * Common instance for {@code empty()}.
 */
private static final Optional<?> EMPTY = new Optional<>();

默认的no-args构造函数是define private,因此您不能创建Optional的实例,除了上面给出的3种方法。

当创建一个Optional时,下面的调用将在末尾发生,并将传递的值分配给'value'属性。

this.value = Objects.requireNonNull(value);

当您尝试从Optional获取值时,如果NoSuchElementException引发了其他情况,则将获取值

public T get() {
    if (value == null) {
        throw new NoSuchElementException("No value present");
    }
    return value;
}

同样,在Optional类中定义的其他函数仅在'value'属性周围运行。浏览Optional.java源代码以获取更多信息。

5)可选试图解决什么?

Optional通过增加考虑到有时缺少返回值来构建更具表现力的API的可能性,尝试减少Java系统中空指针异常的数量。如果Optional从一开始就存在,那么今天大多数库和应用程序可能会更好地处理缺少的返回值,从而减少了空指针异常的数量以及总体上的错误总数。

通过使用Optional,用户不得不考虑特殊情况。除了为null命名而提高了可读性之外,Optional最大优点是它的防白痴能力如果您要完全编译程序,它会迫使您积极考虑不存在的情况,因为您必须积极展开Optional并解决该失败情况。

6)Optional不尝试解决的问题是什么?

可选并不是避免所有类型的空指针的机制例如,仍然必须测试方法和构造函数的强制输入参数。

像使用null时一样,Optional不能帮助传达缺失值的含义。因此,该方法的调用者仍然必须检查API的javadoc以了解缺少Optional的含义,以便正确处理它。

请注意,这Optional并不是要在以下情况下使用,因为它可能不会给我们带来任何好处:

  • 在域模型层中(不可序列化)
  • 在DTO中(不可序列化)
  • 在方法的输入参数中
  • 在构造函数参数中

7)应该如何使用可选的?

几乎所有时候都应该使用Optional 作为可能不返回值的函数的返回类型

这是来自OpenJDK邮件列表的报价:

JSR-335 EG非常强烈地认为Optional不应仅用于支持option-return成语。
有人建议甚至将其重命名为“ OptionalReturn ”。

从本质上讲,这意味着Optional仅当它们确实达到目的时才应将其用作某些服务,存储库或实用程序方法的返回类型。

8)结论

在本文中,我们了解了如何采用新的Java SE 8java.util.OptionalOptional的目的不是替换代码库中的每个单个null引用,而是帮助您设计更好的API,在这些API中,仅通过读取方法的签名,用户就可以知道是否期望可选值并适当地对其进行处理。 。

这就是这个很棒的功能。在评论部分让我知道您的想法。



正文到此结束