1. 简介
在本教程中,我们将讨论使用Optional作为记录参数的可能性以及为什么这是一种不好的做法。
2. Optional的预期用途
在讨论Optional和记录的关系之前,让我们快速回顾一下Java中Optional的预期用途。
通常,在Java 8之前,我们使用null来表示对象的空状态。但是,将null作为返回值需要在运行时从调用方代码进行null检查验证。如果调用方未进行验证,则可能会收到NullPointerException,获取异常有时用于识别值的缺失。
Optional的主要目标是表示一个表示值缺失的方法返回值,我们可以使用Optional作为返回值,而不是让我们的应用程序因NullPointerException而崩溃,以识别值的缺失。因此,我们在编译时就知道返回值是包含内容还是不包含内容。
此外,正如Java文档中所述:
Optional旨在为库方法返回类型提供一种有限的机制,这种情况下,明确需要表示“无结果”,而使用null极有可能导致错误
因此,还必须注意Optional不打算做什么。在这方面,我们可以强调Optional不打算用作任何类的实例字段。
3. Java记录的用例
我们还可以了解一些有关记录的概念,以便更好地了解如何使用Optional作为记录参数。
记录只是数据载体,当我们想将数据从一个地方传输到另一个地方时,它很适合,例如从数据库传输到我们的应用程序。
让我们解释一下JEP-395:
记录是作为不可变数据的透明载体的类。
记录的一个关键定义是它们是不可变的,因此,一旦我们实例化一条记录,其所有数据在程序的其余部分都保持不可修改。这对于传输数据的对象来说非常好,因为不可变对象不容易出错。
记录还会自动定义具有相同字段名称的访问器方法,因此,通过定义它们,我们可以获得与定义字段相同的Getter。
JDK记录定义还表明记录所保存的数据应该是透明的。因此,如果我们调用访问器方法,我们应该获得有效数据。在这种情况下,有效数据意味着真正代表对象状态的值。让我们用Project Amber的话来解释一下:
数据类(记录)的API对状态、整个状态以及仅状态进行建模。
不变性和透明度是捍卫记录不能有可选参数这一论点的基本定义。
4. Optional作为记录参数
现在我们对这两个概念有了更好的理解,我们将明白为什么必须避免使用Optional作为记录参数。
首先我们定义一个记录示例:
public record Product(String name, double price, String description) {
}
我们为Product定义了一个数据持有者,其中包含name、price和description。我们可以想象数据持有者是由数据库查询或HTTP调用产生的。
现在,我们假设有时未设置产品描述。在这种情况下,描述可为空。解决该问题的一种方法是将description字段包装到Optional对象中:
public record Product(String name, double price, Optional<String> description) {
}
尽管上述代码编译正确,但我们破坏了产品记录的数据透明度。
此外,记录不可变性使得处理Optional实例比处理空变量更困难,让我们通过一个简单的测试来实际看看:
@Test
public void givenRecordCreationWithOptional_thenCreateItProperly() {
var emptyDescriptionProduct = new Product("television", 1699.99, Optional.empty());
Assertions.assertEquals("television", emptyDescriptionProduct.name());
Assertions.assertEquals(1699.99, emptyDescriptionProduct.price());
Assertions.assertNull(emptyDescriptionProduct.description().orElse(null));
}
我们创建了一个具有一些值的产品,并使用生成的Getter来断言记录已正确实例化。
在我们的产品中,我们定义了变量name、price和description。但是,由于description是Optional,因此我们在检索它之后不会立即获得值。我们需要做一些逻辑来打开它以获取值。换句话说,我们在调用访问器方法后没有获得正确的对象状态。因此,它破坏了Java记录数据透明度的定义。
在这种情况下,我们可能会想,我们该如何处理null呢?好吧,我们可以简单地让它们存在。null表示对象的空状态,在这种情况下,它比空的Optional实例更有意义。在这些情况下,我们可以使用@Nullable注解或其他处理null的良好做法来通知Product类的用户description可以为空。
由于记录字段是不可变的,因此description字段不能更改。因此,要检索description值,我们有一些选择。一种是使用orElse()和orElseGet()打开它或返回默认值。另一种方法是盲目使用get(),如果没有值,它会抛出NoSuchElementException。第三种方法是使用orElseThrow(),如果里面没有任何内容,则抛出错误。
在任何可能的处理方式中,Optional都没有意义,因为在任何情况下,我们要么返回null,要么抛出错误。让description成为一个可空的String会更简单。
5. Optional作为返回类型
在本节中,我们将探讨如何有效地使用Optional作为返回类型。让我们考虑一个场景,其中我们有一个代表用户信息的用户记录:
public record User(String username, String email, String phoneNumber) {
public Optional<String> getOptionalPhoneNumber() {
return Optional.ofNullable(phoneNumber);
}
}
在此示例中,我们有一个用户记录,其中包含username、email和phoneNumber字段。getOptionalPhoneNumber()方法返回一个Optional<String>,表示电话号码可能存在也可能不存在:
@Test
public void givenRecordCreationWithNullOptional_thenReturnOptional() {
User user = new User("john_doe", "john@example.com", null);
Optional<String> optionalPhoneNumber = user.getOptionalPhoneNumber();
Assertions.assertEquals(Optional.empty(), optionalPhoneNumber);
}
通过返回Optional,我们可以决定如何响应-是否提示用户输入电话号码、显示默认消息、或者继续:
optionalPhoneNumber.ifPresentOrElse(
phone -> // handle if phone present
() -> // handle if phone is absent
);
以这种方式使用Optional作为返回类型有助于通过明确解决值缺失问题来提高代码的安全性。它提供了一种避免NullPointerException的方法,并鼓励更好地处理可能缺失的信息。
6. 总结
在本文中,我们了解了Java记录的定义并了解了透明度和不变性的重要性。
我们还研究了Optional类的预期用途,更重要的是,我们讨论了Optional不适合用作记录参数。相反,Optional可以更有效地用作方法中的返回类型。
Post Directory
