密封类在'Scala编程'中描述,但密封特征不是。 我在哪里可以找到有关密封特性的更多信息?
我想知道,如果密封的特性与密封类相同吗? 或者,如果没有,有什么区别? 什么时候使用密封特性是个好主意(何时不是)?
答案 0 :(得分:434)
sealed
特征只能在与其声明相同的文件中扩展。
它们通常用于提供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
因此,如果可能的子类型数量是有限的且事先已知,则应使用密封特征(或密封的抽象类)。有关更多示例,您可以查看list和option实现。
答案 1 :(得分:86)
密封特性与密封类相同吗?
就sealed
而言,是的。当然,他们会分享trait
和class
之间的正常差异。
或者,如果没有,有什么区别?
摸拟。
何时使用密封特性(何时不使用)是个好主意?
如果您有sealed class X
,那么您必须检查X
以及任何子类。 sealed abstract class X
或sealed trait X
也是如此。所以你可以做sealed abstract class X
,但这比trait
更加冗长而且没什么好处。
在abstract class
上使用trait
的主要优点是它可以接收参数。使用类型类时,这一优势尤为重要。例如,假设您要构建一个排序树。你可以这样写:
sealed abstract class Tree[T : Ordering]
但你不能这样做:
sealed trait Tree[T : Ordering]
因为上下文边界(和视图边界)是使用隐式参数实现的。鉴于特征不能接收参数,你不能这样做。
就个人而言,我更喜欢sealed trait
并使用它,除非某些特殊原因让我使用sealed abstract class
。我不是在谈论微妙的原因,而是在面对面的原因,你不能忽视,比如使用类型类。
答案 2 :(得分:52)
当特征被“密封”时,其所有子类都在其中声明 相同的文件,这使得子类集有限,允许 某些编译器检查。
答案 3 :(得分:27)
此外,我觉得有必要指出您的规格:
密封修改器适用于课程定义。 密封类可能无法直接继承,除非继承模板在同一源中定义 file作为继承的类。但是,密封类的子类可以在任何地方继承。
- M. Odersky. The Scala language specification, version 2.8. online, Sept., 2013.
答案 4 :(得分:3)
简而言之:
答案 5 :(得分:0)
还可以密封定义特质,并且只能通过一组固定的case classes
进行扩展。
正常性状和密封性状之间的核心区别可以概括如下:
普通特征是开放的,因此,只要它们提供以下特征,任何数量的类都可以从该特征继承 所有必需的方法以及这些类的实例可以通过特征的属性互换使用 必需的方法。 正常特征层次结构可轻松添加其他子类:只需定义您的类并 实施必要的方法。但是,这使得添加新方法变得很困难: 需要将方法添加到所有现有的子类中,其中可能有很多。
密封特征:它们仅允许一组固定的类从它们继承,并且所有 必须在同一文件或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
。
JSON
的值只能是JSON
空,布尔值,数字,字符串,数组或字典。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