具有可为空组件的 Java 记录

Lei*_*ngo 8 java optional preview-feature java-14 java-record

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

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

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

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

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

因为我认为Optional现在首选使用s 作为返回类型,所以我真的很想知道为什么会有这个限制。我对用法的理解有误吗?如何在不添加另一个带有不隐藏默认签名的签名的访问器的情况下实现相同的目标?如果Optional不是在这种情况下,在所有被使用?

Nam*_*man 8

Arecord包含主要定义其状态的属性。访问器、构造器等的推导完全基于记录的这种状态。

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

当然,正如您提到的,其中一种处理方法是在记录定义本身中包含一个自定义实现

record MyClass(String id, String value) {
    
    Optional<String> getValue() {
        return Optional.ofNullable(value());
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • @Leikingo 这是一个重新思考“value”是否真的必须为空的机会。或者您使用类似“interface MyRecord { String id(); 可选&lt;String&gt; value(); } 记录 MyRecordNoValue(String id) 实现 MyRecord { publicOptional&lt;String&gt; value(){ returnOptional.empty(); } } } 记录 MyRecordWithValue(String id,StringactualValue) 实现 MyRecord { publicOptional&lt;String&gt; value(){ returnOptional.of(actualValue); } } }` (3认同)
  • 虽然我理解其合理性,但我认为当访问器返回可选作为默认实现时,真实状态也会得到反映。包装/通过额外的 API 和附加方法访问记录成员,破坏了简洁的论点,并在消费者方面添加了样板或混乱。也许真正的问题是Optional 的使用不一致,即使在JDK 中也是如此。我认为应该避免返回“null”,但也许我还应该继续使用“null”并完全避免包装......但是在Java中检查“null”并不是很方便。 (2认同)
  • @Leikingo,“value”的真实状态是“null”,它如何用“Optional”表示?这里要考虑的一点是,不要混合认为属性的缺失与为其分配“null”值相同。“Optional”的使用更多的是数据载体本身不会公开的 API,它的上层(通常开发为数据访问层)封装了特定于应用程序的“null”值解释的方式。 (2认同)
  • @Naman该方法假设您始终将“MyRecordWithValue”与非“null”值一起使用,否则使用“MyRecordNoValue”。您可以扩展构造函数来强制执行它和/或添加自动委托给正确结果类型的工厂方法。这些文物不符合最初的评论。无论如何,这只是一个值得深思的问题,而不是一个完整的解决方案。 (2认同)

lpa*_*zic 5

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

record MyRecord (String id, Optional<String> value){
}
Run Code Online (Sandbox Code Playgroud)

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

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

    MyRecord(String id, Optional<String> value) {
        this.id = id;
        this.value = Objects.requireNonNull(value);
    }
}
Run Code Online (Sandbox Code Playgroud)

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