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

2022-09-03 13:16:47

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

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

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

因此,我将验证提取到它自己的工厂中,并编写了类似如下的代码(工厂类和同一包中的记录):

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)

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

为什么记录存在此限制?将来是否有任何计划取消此限制?


答案 1

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

有人提出了这个问题:

规范构造函数必须与记录具有相同的访问权限的原因究竟是什么?

给出的答案是(着重号是我的):

记录被命名为元组,它们仅由其组件以透明的方式定义,即没有封装。从元组,您可以访问每个组件的值,并且可以从所有组件值中创建元组。这个想法是,在方法中,如果你能够看到一条记录,你可以创建它。因此,规范构造函数与记录本身具有相同的可见性。

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


答案 2

问:为什么记录存在此限制?

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

“因为记录在语义上声称是其数据的透明载体......”

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

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

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

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


问:将来是否有任何计划取消此限制?

我不知道任何。没有(公开的)开放Java Bug或RFE关于此。

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


推荐