Java Optional as Return Type – 作为返回类型的Java可选性

最后修改: 2019年 6月 10日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

The Optional type was introduced in Java 8.  It provides a clear and explicit way to convey the message that there may not be a value, without using null.

Optional类型是在Java 8中引入的。 它提供了一种清晰明确的方式来传达可能没有一个值的信息,而无需使用null

When getting an Optional return type, we’re likely to check if the value is missing, leading to fewer NullPointerExceptions in the applications. However, the Optional type isn’t suitable in all places.

当得到一个Optional返回类型时,我们很可能会检查值是否丢失,从而导致应用程序中的NullPointerExceptions减少。然而,Optional类型并不适合在所有地方使用。

Although we can use it wherever we see fit, in this tutorial, we’ll focus on some best practices of using Optional as a return type.

虽然我们可以在任何我们认为合适的地方使用它,但在本教程中,我们将重点介绍使用Optional作为返回类型的一些最佳实践。

2. Optional as Return Type

2.可选的作为返回类型

An Optional type can be a return type for most methods except some scenarios discussed later in the tutorial.

Optional类型可以是大多数方法的返回类型,除了本教程后面讨论的一些情况。

Most of the time, returning an Optional is just fine:

大多数时候,返回一个Optional就可以了。

public static Optional<User> findUserByName(String name) {
    User user = usersByName.get(name);
    Optional<User> opt = Optional.ofNullable(user);
    return opt;
}

This is handy since we can use the Optional API in the calling method:

这很方便,因为我们可以在调用方法中使用OptionalAPI。

public static void changeUserName(String oldFirstName, String newFirstName) {
    findUserByFirstName(oldFirstName).ifPresent(user -> user.setFirstName(newFirstName));
}

It’s also appropriate for a static method or utility method to return an Optional value.  However, there are many situations where we should not return an Optional type.

对于静态方法或实用方法来说,返回一个Optional值也是合适的。 然而,在很多情况下,我们不应该返回一个Optional类型。

3. When to Not Return Optional

3.何时不返回选择

Because Optional is a wrapper and value-based class, there are some operations that can’t be done against Optional object. Many times, it’s just simply better to return the actual type rather than an Optional type.

因为Optional是一个封装和基于值的类,有一些操作不能针对Optional对象进行。很多时候,只是简单地返回实际的类型,而不是返回Optional类型更好。

Generally speaking, for getters in POJOs, it’s more suitable to return the actual type, not an Optional type. Particularly, it’s important for Entity Beans, Data Models, and DTOs to have traditional getters.

一般来说,对于POJO中的getters,更适合返回实际类型,而不是一个Optional类型。特别是,对于实体Bean、数据模型和DTO来说,拥有传统的getters非常重要。

We’ll examine some of the important use cases below.

我们将在下面研究一些重要的用例。

3.1. Serialization

3.1.序列化

Let’s imagine we have a simple entity:

让我们想象一下,我们有一个简单的实体。

public class Sock implements Serializable {
    Integer size;
    Optional<Sock> pair;

    // ... getters and setters
}

This actually won’t work at all. If we were to try and serialize this, we’d get an NotSerializableException:

这实际上是完全行不通的。如果我们要尝试将其序列化,我们会得到一个NotSerializableException

new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(new Sock());

And really, while serializing Optional may work with other libraries, it certainly adds what may be unnecessary complexity.

事实上,虽然序列化Optional可能与其他库一起工作它肯定会增加可能是不必要的复杂性。

Let’s take a look at another application of this same serialization mismatch, this time with JSON.

让我们来看看这个相同的序列化错配的另一个应用,这次是用JSON。

3.2. JSON

3.2 JSON

Modern applications convert Java objects to JSON all the time. If a getter returns an Optional type, we’ll most likely see some unexpected data structure in the final JSON.

现代应用程序一直在将Java对象转换为JSON。如果一个getter返回一个Optional类型,我们很可能在最终的JSON中看到一些意外的数据结构。

Let’s say we have a bean with an optional property:

比方说,我们有一个带有可选属性的bean。

private String firstName;

public Optional<String> getFirstName() {
    return Optional.ofNullable(firstName);
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

So, if we use Jackson to serialize an instance of Optional, we’ll get:

所以,如果我们用Jackson来序列化一个Optional的实例,我们会得到。

{"firstName":{"present":true}}

But, what we’d really want is:

但是,我们真正想要的是。

{"firstName":"Baeldung"}

So, Optional is a pain for serialization use cases. Next, let’s look at the cousin to serialization: writing data to a database.

因此,对于序列化的用例来说,Optional是一种痛苦。接下来,让我们来看看序列化的另一个分支。将数据写入数据库

3.3. JPA

3.3 JPA

In JPA, the getter, setter, and field should have name as well as type agreement. For example, a firstName field of type String should be paired with a getter called getFirstName that also returns a String.

在JPA中,getter、setter和field应该有名称以及类型的一致。例如,一个firstName类型的String字段应该与一个名为getFirstName的getter配对,该getter也返回String。

Following this convention makes several things simpler, including the use of reflection by libraries like Hibernate, to give us great Object-Relational mapping support.

遵循这个惯例可以使一些事情变得更简单,包括像Hibernate这样的库对反射的使用,为我们提供伟大的对象-关系映射支持。

Let’s take a look at our same use case of an optional first name in a POJO.

让我们来看看我们同样的用例:POJO中的可选名字。

This time, though, it’ll be a JPA entity:

不过这一次,它将是一个JPA实体。

@Entity
public class UserOptionalField implements Serializable {
    @Id
    private long userId;

    private Optional<String> firstName;

    // ... getters and setters
}

And let’s go ahead and try to persist it:

并让我们继续努力坚持下去。

UserOptionalField user = new UserOptionalField();
user.setUserId(1l);
user.setFirstName(Optional.of("Baeldung"));
entityManager.persist(user);

Sadly, we run into an error:

可悲的是,我们遇到了一个错误。

Caused by: javax.persistence.PersistenceException: [PersistenceUnit: com.baeldung.optionalReturnType] Unable to build Hibernate SessionFactory
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:1015)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:941)
	at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:56)
	at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:79)
	at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54)
	at com.baeldung.optionalReturnType.PersistOptionalTypeExample.<clinit>(PersistOptionalTypeExample.java:11)
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.Optional, at table: UserOptionalField, for columns: [org.hibernate.mapping.Column(firstName)]

We could try deviating from this standard. For example, we could keep the property as a String, but change the getter:

我们可以尝试偏离这一标准。例如,我们可以将该属性保留为String,但改变getter。

@Column(nullable = true) 
private String firstName; 

public Optional<String> getFirstName() { 
    return Optional.ofNullable(firstName); 
}

It appears that we could have both ways: have an Optional return type for the getter and a persistable field firstName.

看起来我们可以有两种方式:为getter提供一个Optional返回类型,以及一个可持续的字段firstName

However, now that we are inconsistent with our getter, setter, and field, it’ll be more difficult to leverage JPA defaults and IDE source code tools.

然而,现在我们的getter、setter和field都不一致了,利用JPA默认值和IDE源代码工具会更加困难。

Until JPA has elegant support of Optional type, we should stick to the traditional code. It’s simpler and better:

在JPA拥有对Optional类型的优雅支持之前,我们应该坚持使用传统的代码。它更简单也更好。

private String firstName;

// ... traditional getter and setter

Let’s finally take a look at how this affects the front end – check to see if the problem we run into sounds familiar.

让我们最后看一下这对前端的影响–看看我们遇到的问题是否听起来很熟悉。

3.4. Expression Languages

3.4.表达式语言

Preparing a DTO for the front-end presents similar difficulties.

为前端准备一个DTO也有类似的困难。

For example, let’s imagine that we are using JSP templating to read our UserOptional DTO’s firstName from the request:

例如,让我们想象一下,我们正在使用JSP模板从请求中读取我们的UserOptionalDTO的firstName

<c:out value="${requestScope.user.firstName}" />

Since it’s an Optional, we’ll not see “Baeldung“. Instead, we’ll see the String representation of the Optional type:

由于它是一个Optional,我们不会看到”Baeldung“。相反,我们将看到String表示的Optional类型。

Optional[Baeldung]

And this isn’t a problem just with JSP. Any templating language, be it Velocity, Freemarker, or something else, will need to add support for this. Until then, let’s continue to keep our DTOs simple.

而且这不仅仅是JSP的问题。任何模板语言,无论是Velocity、Freemarker,还是其他什么,都需要增加对它的支持。在那之前,让我们继续保持我们的DTOs简单。

4. Conclusion

4.总结

In this tutorial, we’ve learned how we can return an Optional object, and how to deal with this kind of return value.

在本教程中,我们已经学习了如何返回一个Optional对象,以及如何处理这种返回值。

On the other hand, we’ve also learned that there are many scenarios that we would be better off to not use Optional return type for a getter. While we can use Optional type as a hint that there might be no non-null value, we should be careful not to overuse the Optional return type, particularly in a getter of an entity bean or a DTO.

另一方面,我们也了解到,在很多情况下,我们最好不要为一个getter使用Optional返回类型。虽然我们可以使用Optional类型作为可能没有非空值的提示,但我们应该注意不要过度使用Optional返回类型,特别是在实体Bean或DTO的getter中。

The source code of the examples in this tutorial can be found on GitHub.

本教程中的例子的源代码可以在GitHub上找到。