什么是封闭性状?

密封类在“Scala编程”中有描述,但密封特征没有。 我在哪里可以找到更多关于密封性状的信息?< / p >

我想知道,一个封闭的特质是否与一个封闭的职业相同? 或者,如果不是,有什么不同? 什么时候使用密封trait是一个好主意(什么时候不是)?< / p >

107237 次浏览

daily-scala博客:

当一个trait被“密封”时,它的所有子类都在 同一个文件,这使得子类的集合是有限的 某些编译器检查

sealed trait只能在与其声明相同的文件中扩展。

它们通常用于提供enums的替代方案。由于它们只能在单个文件中扩展,所以编译器知道每一种可能的子类型,并可以进行推理。

例如声明:

sealed trait Answer
case object Yes extends Answer
case object No extends Answer

如果匹配不是穷尽的,编译器会发出警告:

scala> val x: Answer = Yes
x: Answer = Yes


scala> x match {
|   case No => println("No")
| }
<console>:12: warning: match is not exhaustive!
missing combination            Yes

因此,如果可能的子类型的数量是有限的并且预先知道,那么您应该使用密封的特征(或密封的抽象类)。更多的例子你可以看看列表选项的实现。

一个封闭的特质和一个封闭的职业是一样的吗?

sealed而言,是的。当然,它们共享traitclass之间的正常差异。

或者,如果不是,有什么不同?

没有实际意义。

什么时候使用密封trait是一个好主意(什么时候不是)?

如果你有一个sealed class X,那么你必须检查X以及任何子类。对于sealed abstract class Xsealed trait X则不是这样。所以你可以执行sealed abstract class X,但这比trait要冗长得多,而且没有什么好处。

使用abstract class而不是trait的主要优点是它可以接收参数。在使用类型类时,这种优势尤其相关。比方说,你想要构建一棵排序树。你可以这样写:

sealed abstract class Tree[T : Ordering]

但你不能这样做:

sealed trait Tree[T : Ordering]

因为上下文边界(和视图边界)是用隐式参数实现的。考虑到trait不能接收参数,你不能这样做。

就我个人而言,我更喜欢sealed trait并使用它,除非有特定的原因使我使用sealed abstract class。我不是在谈论微妙的原因,而是您不能忽视的直接原因,例如使用类型类。

此外,我觉得有必要向您指出规格:

密封修饰符应用于类定义。密封类不能直接继承,除非继承的模板定义在同一个源代码中 文件作为继承的类。但是,密封类的子类可以在任何地方继承

——Odersky。Scala语言规范,2.8版。2013年9月。

‌‌简要:

  • 密封特征只能在同一个文件中扩展
  • 列表可以让编译器很容易地知道所有可能的子类型
  • 当可能的子类型数量有限且事先知道时,使用密封性状
  • 一种在Java中创建枚举的方法
  • 帮助定义代数数据类型(adt)

和更多细节 Scala中关于密封特征的一切 < / p >

trait也可以定义为密封,并且只能由case classes的固定集合扩展。 正常的特征密封的特点的核心区别可以概括如下:

  • 正常的特征是开放的,所以任何数量的类都可以继承该trait,只要它们提供了 所有必需的方法和这些类的实例都可以通过trait互换使用 所需的方法。 正常的特征层次结构使添加额外的子类变得容易:只需定义你的类和 实现必要的方法。然而,这使得添加新方法变得困难:一个新的 方法需要添加到所有现有的子类中,这些子类可能有很多

  • 密封的特点是封闭的:它们只允许一个固定的类集从它们继承,并且所有 继承类必须与trait本身一起定义在同一个文件或REPL命令中。 密封的特点层次结构则相反:添加新方法很容易,因为新方法可以 简单地对每个子类进行模式匹配,并决定它想为每个子类做什么。然而,添加 新建子类比较困难,因为您需要找到所有现有的模式匹配,并将案例添加到其中 处理你的新子类

举个例子

object SealedTraits extends App{
sealed trait Point
case class Point2D(x: Double, y: Double) extends Point
case class Point3D(x: Double, y: Double, z: Double) extends Point


def hypotenuse(p: Point) = p match {
case Point2D(x, y) => math.sqrt(x  x + y  y)
case Point3D(x, y, z) => math.sqrt(x  x + y  y + z  z)
}


val points: Array[Point] = Array(Point2D(1, 2), Point3D(4, 5, 6))


for (p <- points) println(hypotenuse(p))
// 2.23606797749979
// 8.774964387392123
一般来说,密封的特点适用于你期望子类数量的层次结构建模 很少改变或根本不改变可以使用密封的特点建模的一个很好的例子是 JSON . < / p >
  • JSON值只能是JSON null、布尔值、数字、字符串、数组或字典。
  • JSON已经20年没有改变了,所以不太可能有人需要扩展我们的JSON
  • 虽然子类的集合是固定的,但我们可能希望在JSON blob上执行的操作范围是 无界:解析它,序列化它,漂亮打印它,缩小它,消毒它,等等。 因此,将JSON数据结构建模为封闭的密封特征是有意义的 . .
  sealed trait Json
case class Null() extends Json
case class Bool(value: Boolean) extends Json
case class Str(value: String) extends Json
case class Num(value: Double) extends Json
case class Arr(value: Seq[Json]) extends Json
case class Dict(value: Map[String, Json]) extends Json