具有可为 null 的组件的 Java 记录

我真的很喜欢在Java 14中添加记录,至少作为预览功能,因为它有助于减少我对简单,不可变的“数据持有者”使用lombok的需求。但是我在实现可为 null 的组件时遇到了问题。我试图避免在我的代码库中返回以指示值可能不存在。因此,我目前经常在龙目岛使用类似以下模式的东西。null

@Value
public class MyClass {
 String id;
 @Nullable String value;

 Optional<String> getValue() { // overwrite the generated getter
  return Optional.ofNullable(this.value);
 }
}

当我现在尝试对记录使用相同的模式时,不允许声明。incorrect component accessor return type

record MyRecord (String id, @Nullable String value){
 Optional<String> value(){
  return Optional.ofNullable(this.value); 
 }
}

由于我认为现在首选使用s作为返回类型,我真的很想知道为什么会有这个限制。我对用法的理解是错误的吗?我如何实现相同的目标,而不添加另一个不隐藏默认签名的具有另一个签名的访问者?在这种情况下根本不应该使用?OptionalOptional


答案 1

A 包含主要定义其状态的属性。访问器、构造函数等的派生完全基于记录的这种状态。record

现在,在您的示例中,属性的状态是 ,因此使用默认实现的访问最终会提供真实状态。为了提供对此属性的自定义访问,您需要寻找一个被覆盖的 API,该 API 包装实际状态并进一步提供返回类型。valuenullOptional

当然,正如你所提到的,处理它的方法之一是将自定义实现包含在记录定义本身中。

record MyClass(String id, String value) {
    
    Optional<String> getValue() {
        return Optional.ofNullable(value());
    }
}

或者,您可以在单独的类中将读取和写入 API 与数据载体分离,并将记录实例传递给它们以进行自定义访问。

JEP 384中最相关的引用:我发现的记录是(格式化我的):

记录声明其状态(变量组),并提交到与该状态匹配的 API。这意味着记录放弃了类通常享有的自由 - 将类的API与其内部表示形式分离的能力 - 但作为回报,记录变得更加简洁。


答案 2

由于对记录的限制,即规范构造函数类型需要与访问器类型匹配,因此与记录一起使用的实用方法是将其定义为属性类型:Optional

record MyRecord (String id, Optional<String> value){
}

有人指出,这是有问题的,因为 null 可能作为值传递给构造函数。这可以通过规范构造函数禁止此类不变量来解决:MyRecord

record MyRecord(String id, Optional<String> value) {

    MyRecord(String id, Optional<String> value) {
        this.id = id;
        this.value = Objects.requireNonNull(value);
    }
}

在实践中,大多数常见的库或框架(例如 Jackson,Spring)都支持识别 Optional 类型并自动将 null 转换为 null,因此这是否是需要在特定实例中解决的问题取决于上下文。我建议在代码库中研究对 Optional 的支持,然后再使代码混乱,可能没有必要。Optional.empty()


推荐