为什么 Java 记录的规范构造函数不能具有比记录级别更严格的访问权限?

Why can a Java record's canonical constructor not have more restrictive access than the record level?

我有一种情况,我希望特定类型的记录实例只能使用同一包内单独 class 中的工厂方法创建。这是因为在创建记录之前我需要执行大量验证。

该记录旨在成为其已验证字段的哑数据载体,但无法在记录的构造函数中进行验证,因为我们需要访问一些精心设计的验证器对象才能实际执行验证。

由于将验证器对象传递给记录构造函数意味着它们将构成记录状态的一部分,这意味着我们不能使用记录构造函数来执行记录的验证。

所以我将验证提取到它自己的工厂中并编写了类似这样的代码(工厂 class 和同一个包中的记录):

package some.package;

// imports.....

@Component
class SomeRecordFactory {
    private final SomeValidator someValidator;
    private final SomeOtherValidator someOtherValidator;
    // Rest of the fields
    // ....

    // constructor  
    // ....


    public SomeRecord create(...) {
         someValidator.validate(....);
         someOtherValidator.validate(....);
         // .... other validation

         return new SomeRecord(...);
    }
}
package some.package;

public record SomeRecord(...) {
    /* package-private */ SomeRecord {
    }
}

无论出于何种原因,上述不适用于 IntelliJ 抱怨:

Compact constructor access level cannot be more restrictive than the record access level (public)

我可以通过使用普通 class(它允许单个包私有构造函数)来避免这个问题,但我想更准确地将数据建模为记录。

为什么对记录存在此限制?将来有没有取消这个限制的计划?

Q: Why does this restriction exist for records?

JEP 359 或 JLS 中没有明确说明该决定的理由,但我认为 JEP 的这段摘录暗示了这一点:

"Because records make the semantic claim of being transparent carriers for their data ..."

“透明载体”意味着(对我1)记录被设计为具有最小的抽象边界。限制构造函数的访问意味着(对我而言)额外的抽象边界。

此外,我怀疑具有更多限制性访问修饰符的记录构造函数 可能 阻碍或复杂化 Java 未来版本中记录的预期用例。

无论如何,我的看法是,如果您想要这样的花哨的东西,您应该声明 class 而不是记录。

1 - 透明与不透明相反,抽象数据类型通常在设计上是不透明的。显然,这只是我对 JEP 作者的意思的理解


Q: Are there any plans to remove this restriction in the future?

我什么都不知道。没有 (public) 个未解决的 Java 关于此的错误或 RFE。

事实上,与此主题相关的所有 JDK 错误都是为了确保 Java 15+ 规范明确了限制。没有迹象表明该限制是意外或疏忽造成的。

我在 amber 邮件列表 (http://mail.openjdk.java.net/pipermail/amber-dev/2020-December.txt) 上问了这个问题。

提出问题:

What exactly is the reason that the canonical constructor must have the same access as the record?

给出的答案是(重点是我加的):

Records are named tuples, they are defined only by their components, in a transparent manner i.e. no encapsulation. From a tuple, you can access to the value of each component and from all component values, you can create a tuple. The idea is that, in a method, if you are able to see a record, you can create it. Thus the canonical constructor has the same visibility as the record itself.

因此存在限制是为了符合设计目标和事实,即如果某人拥有记录的实例,他们应该能够解构它,然后使用规范构造函数重建它。当然,作为必然结果,这需要规范构造函数具有与记录本身相同的访问权限。