tl;博士
- 技术限制:多重继承可防止与超类混合。
Enum
Record
- 解决办法:保留枚举的“作为成员”字段
record
NamedIdentity.JILL.detail.nickname() // ➣ "Jill the Archeress"
多重继承
你问:
枚举记录不存在是有原因的吗?
技术原因是,在Java中,每个枚举都是Enum
类的子类,而每个记录都是Record
类的子类。
Java 不支持多重继承。所以两者不能结合起来。
语义学
但更重要的是语义。
枚举用于在编译时声明一组有限的命名实例。每个枚举对象在枚举类加载时实例化。在运行时,我们无法实例化任何更多的 hat 类对象(好吧,也许使用极端反射/内省代码,但我会忽略它)。
Java 中的记录不会自动实例化。您的代码通过调用 来实例化该类的任意多个对象。所以完全不一样。new
样板减少不是目标record
你说:
我以为Java 14的记录关键字会为我节省样板代码
您误解了功能的目的。我建议你阅读JEP 395,并观看Brian Goetz关于这个主题的最新演讲。record
正如Johannes Kuhn所评论的那样,其目标不是减少样板。这种减少是一种令人愉快的副作用,但不是发明的原因。record
record
记录在形式上意味着“名义元组”。
-
元组是指按特定顺序排列的各种类型值的集合,或者如维基百科所说:“元素的有限有序列表(序列)”。
-
名义表示每个元素都有一个名称。
记录意味着是一个简单而透明的数据载体。透明表示其所有成员字段都已公开。它的 getter 方法只是简单地命名为与字段相同的方法,而不使用 JavaBeans 的约定。的默认植入是检查每个成员字段。记录的目的是关注正在携带的数据,而不是行为(方法)。get…
hashCode
equals
此外,记录意味着浅层不可变。不可变意味着您无法更改记录实例中的基元值,也无法更改对象引用。记录实例中的对象本身可能是可变的,这就是我们所说的浅层。但是,无法更改记录本身字段的值(基元值或对象引用)。不能将替换对象重新指定为记录的成员字段之一。
- 如果在编译时已知有一组有限的实例,请使用 。
enum
- 当您编写一个类时,其主要工作是以不可变且透明的方式携带一组数据字段,请使用 。
record
值得一提的问题
我可以看到这两个概念在哪里可以相交,在编译时我们知道一组有限的不可变的透明命名值集合。所以你的问题是有效的,但不是因为样板减少。询问Brian Goetz或Java团队中的其他人是否曾经讨论过这个概念会很有趣。
解决办法:将 a 存储在您的record
enum
解决方法非常简单:在枚举中保留一个记录
实例。
您可以将记录传递给枚举构造函数,并将该记录存储为枚举定义上的成员字段。
将“成员”字段设为 。这使得我们的枚举不可变。因此,无需标记私有,也无需添加 getter 方法。final
首先是定义。record
package org.example;
public record Performer(int id , String nickname)
{
}
接下来,我们将 的实例传递给枚举构造函数。record
package org.example;
public enum NamedIdentity
{
JOHN( new Performer( 1 , "John the Slayer" ) ),
JILL( new Performer( 2 , "Jill the Archeress" ) );
final Performer performer;
NamedIdentity ( final Performer performer ) { this.performer = performer; }
}
如果唯一在枚举的上下文中有意义,我们可以将两者嵌套在一起,而不是使用单独的文件。该功能在构建时考虑了嵌套,并且在那里运行良好。record
.java
record
在某些情况下,命名嵌套可能很棘手。我想如果没有更好的名字是显而易见的,像这样的东西可能会作为一个普通的通用标签。record
Detail
package org.example;
public enum NamedIdentity
{
JOHN( new Performer( 1 , "John the Slayer" ) ),
JILL( new Performer( 2 , "Jill the Archeress" ) );
final Performer performer;
NamedIdentity ( final Performer performer ) { this.performer = performer; }
public record Performer(int id , String nickname) {}
}
在我看来,这种解决方法是一个可靠的解决方案。我们通过减少样板的优点使代码变得清晰。我喜欢将其作为在枚举上保留一堆数据字段的一般替代,因为使用a会使意图明确而明显。我希望在我未来的工作中使用它,感谢你的问题。record
让我们来执行该代码。
for ( NamedIdentity namedIdentity : NamedIdentity.values() )
{
System.out.println( "---------------------" );
System.out.println( "enum name: " + namedIdentity.name() );
System.out.println( "id: " + namedIdentity.performer.id() );
System.out.println( "nickname: " + namedIdentity.performer.nickname() );
}
System.out.println( "---------------------" );
运行时。
---------------------
enum name: JOHN
id: 1
nickname: John the Slayer
---------------------
enum name: JILL
id: 2
nickname: Jill the Archeress
---------------------
本地声明
仅供参考,现在在Java 16 +中,我们可以在本地声明枚举,记录和接口。这是创建记录功能所做工作的一部分。见JEP 395:记录。
因此,枚举、记录和接口可以在以下三个级别中的任何一个级别声明:
- 在他们自己的文件中。
.java.
- 嵌套在类中。
- 在方法中本地。
我顺便提到这一点,与这个问题没有直接关系。