在2019年4月,Java语言架构师Brian Goetz提交了一份JEP草案,提议向Java添加两个新功能 。 现在,草案全部长大了,我们有两个新的JEP。 JEP 359和JEP360。在本文中,我们将重点介绍JEP 360 Records(预览) 。
JEP 360:密封类型
JEP 360旨在通过引入密封类型来增强Java。 Brain Goetz将它们描述为“对其他类或接口可以扩展或实现它们的类型施加限制的类或接口。”
该提案有两个目标。 一:限制哪些类可以是子类型。 二:密封类型可以允许“在使用现场进行详尽的分析 ,例如在为密封类型实例切换类型模式时。”
建议的语法具有可选的allows子句,如下所示:
sealed interface Node
permits A, B, C { ... }
通过包含一个显式的allows子句,Node只能由该子句中列出的类型扩展,这些类型也必须是同一模块的成员,或者,如果是未命名的模块,则必须是同一包。 Goetz指出,在许多情况下这可能是过大的,因为如果所有子类型都在同一编译单元中声明,则不需要allows子句,因为在这种情况下,编译器通过枚举在同一编译中声明的子类型来推断allows子句单元。
密封类型的子类和lambda不能是匿名的,并且除非使用非密封修饰符明确声明,否则密封类型的抽象子类型是隐式密封的。 除非另外明确声明,否则密封类型的具体子类型是隐式最终的。 像最终性一样,密封由语言编译器和JVM强制执行。
动机,目标和非目标
Goetz在JEP 360中编写了以下内容:
Java的类型系统使我们能够回答诸如“圆形是一种形状吗?”之类的问题,但是对于诸如“什么形状是什么样的形状?”之类的问题却给予的帮助却少得多。 当限制一个类可以实现一个库的类型时,类型系统对库毫无帮助。 这意味着即使库作者的设计意图是给定类型仅具有一组特定的子类型,也无法强制执行此操作,因此该实现不能假定该类型。 (访问控制使库作者可以约束哪些软件包可以访问并因此实现库,但不能将实现者与用户区分开。) Brian Goetz
目的是能够在类型系统中捕获诸如“形状是圆形还是矩形”这样的语句,因为它不仅为其他用户提供了有用的文档,而且还允许Java编译器执行更好的类型检查,因为它可以详尽地枚举已知的子类型。 它还将使库作者可以预测和了解已知实现的行为。 密封类型也是模式匹配的穷举信息的有用来源。
JEP 360并不打算提供诸如“朋友”之类的新形式的访问控制,或者提供对成员覆盖的精细控制。
现在,让我们更详细地了解密封类型。
语法
JEP 360提供了以下密封型语法示例:
NormalClassDeclaration:
{ClassModifier} class TypeIdentifier [TypeParameters]
[Superclass] [Superinterfaces] [PermittedSubclasses] ClassBody</pre>
ClassModifier:
(one of)
Annotation public protected private
abstract static sealed final non-sealed strictfp
PermittedSubclasses:
permits ClassTypeList
ClassTypeList:
ClassType {, ClassType}
限制条件
Goetz列出了三个限制。 如果未密封的修饰符没有密封的超类型出现在类型上,这是一个错误;如果在任何类型声明中出现多个final,密封或非密封的一个以上,则是一个错误;如果允许,则是一个错误子句出现在未声明为密封的类型上。
密封形式的编译形式
密封类型的class
文件必须具有PermittedSubtypes
属性,该属性列出了允许的子类型。 “如果源代码中密封类型的声明缺少显式的allows子句,则在编译时将一组允许的子类型计算为那些在与密封类型相同的编译单元中声明的密封类型的子类型。” 提供以下示例:
PermittedSubtypes_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 permitted_subtypes_count;
u2 classes[permitted_subtypes_count];
}
还请参见:
反射API
JEP进一步指出,它将在java.lang.Class中添加以下公共方法:
-
java.lang.constant.ClassDesc<?>[]
getPermittedSubtypes()
-
boolean isSealed()
方法getPermittedSubtypes()
返回一个包含java.lang.constant.ClassDesc对象的数组,如果该对象是密封的,则表示该类的所有允许的子类型;如果未密封,则返回一个空数组。
如果给定的类或接口是密封的,则isSealed
方法将返回true。
备择方案
JEP 360指出,某些语言直接支持代数数据类型,实际上,对于Java开发人员来说, enum
的变体可能是更熟悉的方式。 但是,这并不适合所有用例,例如,总和超出一个编译单元以上的类,或者总和超出非乘积类的那些用例。 因此,稍微不熟悉是为更全面的功能付出的代价很小。
类还通过其构造函数支持状态验证,而元组则不支持。 而且类可以具有与其状态相关的行为,而元组作为原始数据无法提供。
依存关系
正如本文开头所提到的,JEP 359和JEP 360在一起是因为它们都来自同一JEP草案。 因此,由于记录可以与密封类型携手工作以形成称为代数数据类型的事实,因此JEP 359被列为依赖项。
有关更多信息,请全面了解JEP 360 。