Lombok的getter/setter与Java 14记录类型

78

我喜欢项目Lombok,但最近我在阅读并尝试一些Java 14的新功能。

其中的一个新特性是record关键字,它允许创建一个已经内置以下功能的类:构造函数、私有不可变字段、访问器、equals/hashCode、getter和toString方法。

现在我的问题是:我们是应该依赖Lombok的功能,还是开始使用record功能:

这样做更好吗:

record Person (String name, String surname) {}

或者那个:

@AllArgsConstructor
@ToString
@EqualsAndHashCode
public class Person {
  @Getter private int name;
  @Getter private int surname;
}

这两种方法的利弊是什么?


7
首先,“record” 不能用于期望使用 JavaBeans 风格的 getter 和 setter 的内容。 - Mark Rotteveel
7
Rotteveel 的评论的意思是,记录(record)上的属性访问方法与该属性同名。因此,使用alice.phoneNumber()而不是像JavaBeans约定那样在前面加上get,比如alice.getPhoneNumber() - Basil Bourque
1
记录与类相比有很多限制,例如记录不能扩展其他记录或类。有关详细信息,请查看此JEP的限制部分 https://openjdk.java.net/jeps/359 - Youssef NAIT
1
你的示例非常偏向于使用record。如果不加上你在旧类中添加的所有冗余代码,那么两者之间的差异就会变得很小:record Person (String name, String surname) {} 与: @Value class Person { int name; int surname; }此外,只需将@Value更改为@Data,你就可以神奇地获得一个与ORM兼容的非不可变类。这是使用Java 17 record不可能或难以实现的。 - Zartc
6个回答

89
Lombok和Java语言的record特性是不同的工具,各有其用。虽然它们存在一些表面上的重叠,但不要被此分散注意力。
Lombok主要关注的是语法便利性;它是一个预装了一些常见代码模式的宏处理器。它不会赋予任何语义;它只是根据您在代码中设置的注释开关自动执行这些模式。 Lombok仅关注实现数据承载类的方便性。
Record是一种语义特性;它们是命名元组。通过声明"Point是(int x, int y)元组",编译器可以从该状态描述中推导出它的表示方式,以及构造、声明、相等性、哈希和字符串表示协议。因为它们具有语义,所以读者和框架也能更加确信地推断记录的API。 (如果这也方便了语法,那就太好了。)

1
+1 Brian Goetz:这还假设您可以将当前的Lombok版本放入IDE中。我想知道Lombok是否有任何有意义的优势,使得代码阅读比类注释更快。 - Trunk
我认为它们并不是完全的命名元组——一般的元组不需要任何定义。但是记录类型仍然需要在使用之前定义。在C#中,可以这样做:(string first, string middle, string last) methodThatReturnsATuple() - Solubris
5
如果你可以定义“元组”,那当然你是正确的。我们曾经就Java 8中的lambda是否是“真正的”闭包进行过同样的辩论;但这归根结底总是关于“我认为‘闭包’意味着X,因为这是我在第一个接触它的语言中所理解的意思。”但这个争论并不是很有成效。 - Brian Goetz
有很多关于元组的合理定义的差异,因此我认为将记录类型与元组以任何方式联系起来都是具有误导性的。例如: 记录点(int x,int y){} 记录偏移量(int x,int y){} var p1 = new Point(1,2); var o1 = new Offset(1,2); System.out.println(p1.equals(o1)); 这将打印出false,但对于元组来说这是没有意义的。 - Solubris
6
我认为在你之前rzwitserloot所做的那种免责声明可能很有用,说明你是Records (https://openjdk.java.net/jeps/384)的作者。与@Value相比,你可能对它们的灵活性和实用性持有一定偏见。 - user3742898

35
注意:可以使用 @Value 注解代替注解的圣诞树。请注意,这将使类变为 final 类型,并使所有字段都变成私有和不可修改的,同时也会提供其余的所有功能。 这与记录非常接近(它们也是 final 的,其中所有字段都是 final 的)。 record 目前仍处于预览阶段,因此对于生产代码来说,显然还不适用。可以使用 lombok。
一旦记录退出预览阶段,情况就更复杂了。Lombok 更加灵活;您可以轻松地交换一些新方面,而无需重写所有代码(例如,您只需添加一个 'extends' 子句到您的类中,而不必手动编写等于和 hashCode 方法; 而记录无法给您)。 Lombok 还提供了更多功能:例如,通过添加 @Builder 注解,您可以添加一个 builder,而记录无法做到这一点。
如果您设计的类高度不可能使用上述任何内容,则应使用记录。
免责声明:我是 Project Lombok 的核心贡献者。

3
@Builder 支持记录类型自 2021 年 10 月的 v1.18.22 版本中添加 — https://github.com/projectlombok/lombok/issues/2356 - M. Justin

14

我也一直在尝试这种组合,通过自己的实践,我列出了以下几点区别:

Lombok

  • 记录(Records)还没有成为完全替代 Lombok 的有力工具。请注意,该库提供的不仅仅是 @Getter, @AllArgsConstructor, @ToString, @EqualsAndHashCode 这些注解。
  • 从个人经验来看,EqualsAndHashCode 和你期望的并不相同,当你考虑迁移到记录(Records)时,请参考 此处的说明

记录(Records)

  • 记录是语言的正式组成部分,支持所有主要的 IDE。
  • 另外,如果您的对象表示的要求是“数据载体”,则您仍然可以利用记录(Records)的优势,而无需依赖于附加库来减少繁琐的代码,以精确执行该操作。这就是为什么在这篇博客的结论中写道:

这也将帮助团队消除许多手工编写实现基础模式的代码,并减少或消除对像 Lombok 这样的库的需求。

当然,日常工作中,根据项目需求选择合适的方式进行实践是明智的。


注意 - 我会尝试不断更新更多的示例,以便用户能够频繁使用它们。 - Naman

10
虽然Brian很好地解释了Lombok和records的不同目标,但他没有解释何时使用哪个。
如果您的类是一个“透明的、浅不可变的数据集合”,那么使用record会做得很好,因为:
1.它比用Lombok注释的类更加简便。
2.它携带着语义信息“这是一个不可变的数据类”(正如Brian Goetz的答案中强调的那样)。这种语义信息对程序员和框架都可能非常有价值。
如果您的类无法满足record的限制(例如不可变性、无builder、不可扩展),我仍然会使用Lombok。

2

Lombok不需要在您的构建代码中添加任何额外的依赖项。 Lombok和records都有优点和缺点,但额外的依赖性不是您应该考虑的因素之一。 - mjaggard

-2
简而言之,如果您正在使用Java进行函数编码,请使用records。否则使用Lombok,它更加灵活和强大。
当然,在API编码等方面有一些例外。

函数式编程对于使用记录或Lombok是否有很小的影响,甚至没有影响。 - mjaggard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接