我一直在使用密封的特征和Case对象在Scala中定义枚举类型,最近我遇到了另一种在Scala中扩展枚举类的方法,如下所示:
object CertificateStatusEnum extends Enumeration {
val Accepted, SignatureError, CertificateExpired, CertificateRevoked, NoCertificateAvailable, CertChainError, ContractCancelled = Value
}
反对做这样的事情:
sealed trait CertificateStatus
object CertificateStatus extends {
case object Accepted extends CertificateStatus
case object SignatureError extends CertificateStatus
case object CertificateExpired extends CertificateStatus
case object CertificateRevoked extends CertificateStatus
case object NoCertificateAvailable extends CertificateStatus
case object CertChainError extends CertificateStatus
case object ContractCancelled extends CertificateStatus
}
什么被认为是好的方法?
2条答案
按热度按时间yduiuuwa1#
它们都是出于简单的目的来完成工作,但就最佳实践而言,
sealed traits
+case objects
的使用更为灵活。背后的故事是,因为Scala附带了Java拥有的一切,所以Java有枚举,Scala出于互操作性的原因不得不把它们放在那里。但Scala不需要它们,因为它支持ADT(代数数据类型),因此可以像您刚才看到的那样以函数方式生成枚举。
对于普通的
Enumeration
类,您将遇到某些限制:String
名称和Int
id之外,扩展元素以容纳更多数据实际上更难,因为Value
是最终的。object CertificateStatusEnum extends Enumeration
时,您的枚举将不会被定义为CertificateStatusEnum
类型,而是定义为CertificateStatusEnum.Value
-因此您必须使用一些类型别名来修复它。这样做的问题是您的同伴的类型仍然是CertificateStatusEnum.Value.type
,因此您最终将使用多个别名来解决这个问题,并且会有一个相当令人困惑的枚举。另一方面,代数数据类型是一种类型安全的替代方法,您可以指定每个元素的形状并对枚举进行编码,您只需要使用
sealed traits
(或abstract classes
)和case objects
精确表示的sum类型即可。这些解决了
Enumeration
类的限制,但您还会遇到其他一些(次要的)缺陷,尽管这些不是很大的限制:id
作为属性添加到sealed trait
中,并提供排序方法。case objects
是可序列化的,如果您需要反序列化您的枚举,也没有简单的方法来从其枚举名反序列化Case对象。您很可能需要编写一个定制的反序列化程序。Enumeration
那样遍历它们。但这并不是一个非常常见的用例。然而,它可以很容易地实现,例如:在实践中,您可以使用
Enumeration
做任何事情,也可以使用sealed trait
+case objects
做任何事情。因此,前者脱离了人们的偏好,转而支持后者。这种比较只与Scala 2有关。在Scala3中,它们将ADT及其泛化版本(GADT)与枚举统一在一个新的强大语法下,有效地为您提供了所需的一切。所以你有充分的理由使用它们。正如盖尔提到的那样,他们成为了一流的实体。
lf5gs5x22#
这取决于您希望从枚举中获得什么。
在第一种情况下,您隐式拥有对项目的订单(通过id属性访问)。重新排序是有后果的。
我更喜欢‘Case Object’,在某些情况下,枚举项可以在构造函数中有额外的信息(例如,带RGB的颜色,而不仅仅是名称)。
另外,我建议使用https://index.scala-lang.org/mrvisser/sealerate或类似的库。这允许迭代所有元素。