介绍
根据 Oracle 文档,Optional 是一个容器对象,可能包含也可能不包含非空值。Java 8 中引入它是为了解决 NullPointerException 的问题。本质上,Optional 是一个包装类,其中包含对其他对象的引用。在这种情况下,对象只是指向内存位置的指针,它也可以指向任何内容。另一种看待它的方式是提供类型级解决方案来表示Optional而不是空引用。
在 Optional 之前
在 Java 8 之前,程序员将返回 null 而不是Optional。有几个
这种方法的缺点。一是没有明确的方法来表达 null 可能是一个特殊值。相比之下,返回一个Optional在API中是一个明确的声明,表明其中可能没有值。如果我们想确保不会出现空指针异常,那么我们需要对每个引用进行显式空检查,如下所示,我们都同意这是很多样板文件。
// Life before Optional
private void getIsoCode( User user){
if (user != null) {
Address address = user.getAddress();
if (address != null) {
Country country = address.getCountry();
if (country != null) {
String isocode = country.getIsocode();
if (isocode != null) {
isocode = isocode.toUpperCase();
}
}
}
}
}
为了简化这个过程,让我们看看如何使用Optional类,从创建和验证实例,到使用它提供的不同方法,并将其与返回相同类型的其他方法结合起来,后者是可选谎言的真正力量。
Optional 的特性
Optional 提供了大约 10 个方法,我们可以使用它们来创建和使用 Optional,下面我们将了解它们的使用方法。
创建一个 Optional
Optional 有三种实例创建方法。
返回一个空的 Optional 实例。
// Creating an empty optional
Optional<String> empty = Optional.empty();
返回一个空的 Optional 实例。此 Optional 不存在任何值。尽管这样做可能很诱人,但请避免通过与 {==} 与 {Option.empty()} 返回的实例进行比较来测试对象是否为空。不能保证它是单例。相反,请使用 {isPresent()}。
返回具有指定的当前非空值的 Optional。
// Creating an optional using of
String name = "java";
Optional<String> opt = Optional.of(name);
静态方法需要一个非空参数,否则会抛出空指针异常。那么,如果我们不知道参数是否为空,那就是当我们使用下面描述的 ofNullable 时。
- static
Optional ofNullable(T value)
如果非空,则返回描述指定值的 Optional ,否则返回空Optional。
// Possible null value
Optional<String> optional = Optional.ofNullable(name());
private String name(){
String name = "Java";
return (name.length() > 5) ? name : null;
}
通过这样做,如果我们传入一个空引用,它不会抛出异常,而是返回一个空的Optional对象:
好的,这些是动态或手动创建选项的三种方法。下一组方法用于检查值是否存在。
- boolean isPresent()
如果存在值则返回 true,否则返回 false。如果包含的对象不为 null,则返回 true,否则返回 false。在对对象执行任何其他操作之前,通常首先对可选对象调用此方法。
//ispresent
Optional<String> optional1 = Optional.of("javaone");
if (optional1.isPresent()){
//Do something, normally a get
}
- boolean isEmpty()
如果存在值则返回 false,否则返回 true。这与 isPresent 相反,仅在 Java 11 及更高版本中可用。
// isempty
Optional<String> optional1 = Optional.of("javaone");
if (optional1.isEmpty()){
//Do something
}
如果存在值,则使用该值调用指定的使用者,否则不执行任何操作。
如果您是 Java 8 的新手,那么您可能想知道什么是消费者,简单来说,消费者是一种接受参数但不返回任何内容的方法。当使用 ifPresent 时,这看起来就像一石二鸟。我们可以进行值存在检查并使用一种方法执行预期操作,如下所示。
// ifpresent
Optional<String> optional1 = Optional.of("javaone");
opt.ifPresent(s -> System.out.println(s.length()));
Optional 提供了另一组用于获取Optional的方法。
如果此 Optional 中存在值,则返回该值,否则抛出 NoSuchElementException。总而言之,我们想要的是存储在Optional中的值,我们可以通过简单地调用get()来获取它。然而,当值为 null 时,此方法会引发异常,此时 orElse() 方法可以发挥作用。
// get
Optional<String> optional1 = Optional.of("javaone");
if (optional1.isPresent()){
String value = optional1.get();
}
如果存在则返回该值,否则返回其他值。
orElse() 方法用于检索包装在Optional实例中的值。它采用一个作为默认值的参数。orElse() 方法返回包装值(如果存在),否则返回其参数:
// orElse
String nullName = null;
String name = Optional.ofNullable(nullName).orElse("default_name");
好像这还不够,可选类继续提供另一种获取值的方法,即使它的 null 称为 orElseGet()
如果存在则返回该值,否则调用 other 并返回该调用的结果。
orElseGet() 方法与 orElse() 类似。但是,如果Optional不存在,它不会采用返回值,而是采用供应商功能接口,该接口被调用并返回调用的值:
// orElseGet
String name = Optional.ofNullable(nullName).orElseGet(() -> "john");
那么 orElse() 和 orElseGet() 有什么区别
乍一看,这两种方法似乎具有相同的效果。然而,事实并非如此。让我们创建一些示例来突出显示两者之间行为的相似性和差异。
首先,让我们看看当对象为空时它们的行为如何:
String text = null;
String defaultText = Optional.ofNullable(text).orElseGet(this::getDefaultValue);
defaultText = Optional.ofNullable(text).orElse(getDefaultValue());
public String getDefaultValue() {
System.out.println("Getting Default Value");
return "Default Value";
}
在上面的示例中,我们将空文本包装在Optional对象内,并尝试使用两种方法中的每一种来获取包装的值。副作用如下:
Getting default value...
Getting default value...
在每种情况下都会调用默认方法。碰巧,当包装值不存在时,orElse() 和 orElseGet() 的工作方式完全相同。
现在让我们运行另一个测试,其中该值存在,并且理想情况下,甚至不应该创建默认值:
String text = "Something";
System.out.println("Using orElseGet:");
String defaultText = Optional.ofNullable(text).orElseGet(this::getDefaultValue);
System.out.println("Using orElse:");
defaultText = Optional.ofNullable(text).orElse(getDefaultValue());
public String getDefaultValue() {
System.out.println("Getting Default Value");
return "Default Value";
}
在上面的示例中,我们不再包装空值,其余代码保持不变。现在让我们看一下运行这段代码的副作用:
Using orElseGet:
Using orElse:
Getting default value...
请注意,当使用 orElseGet() 检索包装的值时,甚至不会调用默认方法,因为包含的值存在。
但是,当使用 orElse() 时,无论包装值是否存在,都会创建默认对象。因此,在这种情况下,我们刚刚创建了一个从未使用过的冗余对象。
在这个简单的示例中,创建默认对象没有显着的成本,因为 JVM 知道如何处理此类对象。然而,当诸如 default 之类的方法必须进行 Web 服务调用甚至查询数据库时,成本就变得非常明显。
使用 Optional 的最佳实践
就像编程语言的任何其他功能一样,它可以被正确使用,也可以被滥用。为了了解使用可选类的最佳方式,需要了解以下内容:
1.它试图解决什么问题
Optional 是一种减少 Java 系统中空指针异常数量的尝试,方法是增加构建更具表现力的 API 的可能性,以解决有时返回值丢失的可能性。
如果 Optional 从一开始就存在,大多数库和应用程序可能会更好地处理丢失的返回值,从而减少空指针异常的数量和总体错误的总数。
2.它不试图解决什么问题
可选并不是一种避免所有类型空指针的机制。例如,方法和构造函数的强制输入参数仍然需要测试。
与使用 null 时一样,Optional 无助于传达缺失值的含义。类似地,null 可以表示许多不同的含义(未找到值等),缺少的Optional也可以表示许多不同的含义。
该方法的调用者仍然需要检查该方法的javadoc以了解缺少的Optional的含义,以便正确处理它。
同样,通过类似的方式,可以在空块中捕获已检查的异常,没有什么可以阻止调用者调用 get() 并继续前进。
3.何时使用
Optional的预期用途主要是作为返回类型。获取此类型的实例后,您可以提取该值(如果存在)或提供替代行为(如果不存在)。
Optional 类的一个非常有用的用例是将其与流或其他返回Optional的方法相结合,以构建流畅的 API。请参阅下面的代码片段
User user = users.stream().findFirst().orElse(new User("default", "1234"));
4.什么时候不使用它
a) 不要将其用作类中的字段,因为它不可序列化
如果您确实需要序列化包含Optional值的对象,Jackson库提供了将Optional视为普通对象的支持。这意味着 Jackson 将空对象视为 null,将具有值的对象视为包含该值的字段。此功能可以在 jackson-modules-java8 项目中找到。
b) 不要将其用作构造函数和方法的参数,因为这会导致代码不必要地复杂。
User user = new User("[email protected]", "1234", Optional.empty());
重要注意事项
这是一门以价值观为基础的课程;在Optional实例上使用身份敏感操作(包括引用相等(==)、身份哈希码或同步)可能会产生不可预测的结果,应避免。
原文链接
https://medium.com/@hopewellmutanda/java-8-optional-usage-and-best-practices-975c41d66822
标签:返回,String,方法,用法,null,Optional,Java8,orElse From: https://www.cnblogs.com/nihaorz/p/17701851.html