我在Java中有这个简单的record:

public record DatePair( LocalDate start , LocalDate end , long days ) {}

我希望所有三个属性(startend和amp;days)都公开可读,但我希望第三个属性(days)在实例化期间自动计算而不是传入.

因此,我添加了一个静态工厂方法:

public record DatePair( LocalDate start , LocalDate end , long days )
{
    public static DatePair of ( LocalDate start , LocalDate end )
    {
        return new DatePair ( start , end , ChronoUnit.DAYS.between ( start , end ) ) ;
    }
}

我希望only这个静态工厂方法用于实例化.我想隐藏构造函数.因此,我显式编写了隐式构造函数,并将其标记为private.

public record DatePair( LocalDate start , LocalDate end , long days )
{
    private DatePair ( LocalDate start , LocalDate end , long days )  // <---- ERROR: Canonical constructor access level cannot be more restrictive than the record access level ('public')
    {
        this.start = start;
        this.end = end;
        this.days = days;
    }

    public static DatePair of ( LocalDate start , LocalDate end )
    {
        return new DatePair ( start , end , ChronoUnit.DAYS.between ( start , end ) ) ;
    }
}

但是将该承包商标记为private会导致编译器错误:

Java:记录DatePair中的规范构造函数无效(试图分配更强的访问权限;是公共的)

??如果编译器禁止将记录标记为private,如何隐藏记录的隐式构造函数?

推荐答案

这基本上是不可能的,因为这不是记录的工作方式.你有may个人能够把它组合在一起,但这不是记录应该做的事情,因此,如果你这样做了,代码将会令人困惑,API将需要相当多的额外文档来解释它不是以你认为的方式工作的,并且future 的Lang功能可能会立即使你的API过时(它感觉过时了,现在工作起来更奇怪).

记录的本质

记录被设计为可分解和可重构的,并本质上支持这些功能,如在中一样,而不需要编写任何代码来实现任何这一点.唱片只是免费获得了所有的东西,但也是有代价的:它们本质上是由它们的"部分"定义的.这有各种各样的效果-它们不能扩展任何东西(因为这意味着它们是由它们的部件和它们的超类型声明的部件的组合定义的,这比预期的要复杂得多),这些部件被视为最终的,您不能让它看起来像是实际上不仅仅是将其部件组合在一起的东西,这就是代码的问题.

让我们把它的‘如果你试图这样做,future 的语言功能将毁了你的一天’这一方面,并专注于解构和with的概念.

是的,这在很大程度上不是Java yet的一部分,但记录功能是专门设计来扩展的,以涵盖解构及其带来的所有功能,工作进展顺利.具体来说,布莱恩·戈茨(Brian Goetz)负责这个功能和各种功能,这些功能扩展到一个更全面的 idea (记录只是那个 idea 的一小部分),他真的很喜欢这个东西,并一再写到它.包括相当完整的功能提案.

具体地说,对于记录,您很快就能够编写这段代码(请注意,与OpenJDK功能提案一样,不要关注语法或诸如‘.但是,这是否意味着‘with’现在是一个关键字?‘--实际的语法是最后一个被充实的东西.

DatePair dp = ....;

DatePair newDp = dp with {
  days = 20;
}

"解构"的概念与构造函数相同,但与之相反:取一个对象并将其拆分成其组成部分.对于记录,这是显而易见的,事实上(这是关键,为什么您不能以这种方式做您想做的事情),baked in-记录通过您为记录列出的元素(因此,这里是startenddays)解构,并且您可能无法更改这一点.

obj with {block;}运算是语法糖,用于:

  • 解构obj.
  • 对于分解生成的每一项,声明一个局部变量.
  • block英里.本地变量是可用的,并且不是最终变量--您可以随意更改.
  • 构造一个与obj类型相同的新对象,使用这些本地变量传递给它的构造函数.

你的 idea 只有在解构函数只分解成LocalDate startLocalDate end,完全不包括long days的情况下才能起作用.它需要记录能够声明:实际上,this是我的构造函数,具有来自记录的组件列表的不同参数,一旦您打开编写自己的构造函数的大门,那么have to也将编写您自己的解构函数.

问题是,目前还没有解构函数的语法.因此,如果Record允许您编写自己的构造函数(使用与Record组件不同的参数列表),这意味着如果将来发布语言特性(如with),则需要解构函数,因此某些记录不支持它.这让Java lang团队很恼火:他们会说:我们引入了with,它已经可以处理所有的记录了!在future ,一旦我们发布了解构函数功能,它也应该适用于具有解构函数的类.

也就是说,虽然我不能说我incredibly%地知道Brian和OpenJDK团队在想什么,但如果他们不是这样想的,我会感到百分百惊讶.

上面深入探讨OpenJDK在不久的将来的Java计划的目的是解释[A]为什么你不能在记录中创建自己的构造函数,以及[B]为什么不会有一个语言特性会出现,除非它是一组非常重要的更新的一部分(包括解构函数语法,除非有使用它的特性,否则不会发生).

好消息!

幸运的是,这里有一些非常简单的替代策略可以使用.

这似乎最符合您的需求,可以在今天的Java中使用:

public record DatePair(LocalDate start, LocalDate end) {
  public long days() {
    return ChronoUnit.DAYS.between(start, end);
  }
}

这使days不再被视为DatePair的组成部分,但that is the point-记录中的组件基本上可以独立于其其他部分进行更改(可能您可以添加代码,然后声明新状态无效,但现在您强制人们同时设置startdays,您不能‘计算出来’,这看起来像是API太糟糕了,您不会想要这样的东西).

它还受到这样一种观念的影响,即现在每次都要计算天数,而不是被"缓存".你可以通过编写自己的缓存来解决这个问题,例如使用番石榴缓存生成器,但对于杀死蚊子来说,这是一个相当大的火箭筒.如果这真的是你需要的计算,它是相对便宜的,我会像这样写它,不担心性能,除非你拿着一份分析器报告,说这几天的计算是关键.

如果这仍然是不能接受的,那么你想要的东西就不是record代表的东西.您不妨问问如何用enum来表示任意字符串.您不能这样做--这不是枚举的意义所在.因此,您的最终结果是:

import lombok.*;

@Value
@lombok.experimental.Accessors(fluent = true)
public class DatePair {
  @With private final LocalDate start, end;
  @With private final long days;

  public DatePair(LocalDate start, LocalDate end) {
    this.start = start;
    this.end = end;
    this.days = ChronoUnit.DAYS.between(start, end);
  }
}

我建议您不要添加访问者行(这会将getStart()变成只有start().尽管有记录,get还是更好(自动补全功能在Java编辑器环境中随处可见,而且更常见).事实上,Java核心库本身做到了这一点,并使用get,见例如java.time.LocalDate).但是,如果你真的想要它--这就是你要做的.或者添加一个lombok.config文件,并在那里说明您想要流畅的访问器名称.

或者让您的IDE生成所有代码,这将是您必须维护的大量代码.我理解在这里使用记录的‘吸引力’,但记录不能做很多事情.他们也不能延长任何期限.他们也不能通过字段的方式来记忆计算出来的东西.

Java相关问答推荐

如何从片段请求数据到活动?在主要活动中单击按钮请求数据?

try Dockerize Maven应用程序,但发布版本21不支持"

当列顺序更改时,Table View列列表的Change. wasPermanted()总是返回假

如何使用解析器组合子解析Java数组类型签名?

为什么Java中的两个日期有差异?

我的scala文件失败了Scala.g4 ANTLR语法

Oracle DUAL表上使用DDL时jOOQ问题的解析'

RxJava PublishSubject缓冲区元素超时

Java List with all combinations of 8 booleans

为什么我的ArrayList索引的索引总是返回-1?

如何打印本系列的第n项y=-(1)-(1+2)+(1+2+3)+(1+2+3+4)-(1+2+3+4+5)...Java中的(1+2+3+4...+n)

如何将Java文档配置为在指定的项目根目录中生成?

Spring Boot&;Docker:无法执行目标org.springframework.boot:spring-boot-maven-plugin:3.2.0:build-image

Android应用程序为错误的显示类型 Select 尺寸文件

如何从命令行编译包中的所有类?

Java类型推断:为什么要编译它?

在WHILE()循环初始化部分中声明和初始化变量的Java语法?

如何在单元测试中获得我的装饰Mapstruct映射器的实例?

javax.crypto-密码对象-提供者服务是如何工作的?

MapStruct记录到记录的映射不起作用