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

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


当前回答

trait也可以被定义为密封的,并且只能通过一组固定的case类进行扩展。 正常性状与封闭性状的核心差异可以概括为:

Normal traits are open, so any number of classes can inherit from the trait as long as they provide all the required methods, and instances of those classes can be used interchangeably via the trait's required methods. A normal trait hierarchy makes it easy to add additional sub-classes: just define your class and implement the necessary methods. However, it makes it difficult to add new methods: a new method needs to be added to all existing subclasses, of which there may be many. Sealed traits are closed: they only allow a fixed set of classes to inherit from them, and all inheriting classes must be defined together with the trait itself in the same file or REPL command. A sealed trait hierarchy is the opposite: it is easy to add new methods, since a new method can simply pattern match on each sub-class and decide what it wants to do for each. However, adding new sub-classes is difficult, as you need to go to all existing pattern matches and add the case to handle your new sub-class.

举个例子

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。

A JSON value can only be JSON null, boolean, number, string, array, or dictionary. JSON has not changed in 20 years, so it is unlikely that anyone will need to extend our JSON with additional subclasses. While the set of sub-classes is fixed, the range of operations we may want to do on a JSON blob is unbounded: parse it, serialize it, pretty-print it, minify it, sanitize it, etc. Thus it makes sense to model a JSON data structure as a closed sealed trait hierarchy rather than a normal open trait hierarchy.

  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

其他回答

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

密封修饰符应用于类定义。密封类不能直接继承,除非继承模板定义在同一个源代码中 文件作为继承的类。但是,密封类的子类可以在任何地方继承。 ——奥德斯基。Scala语言规范,2.8版。2013年9月。

来自daily-scala博客:

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

trait也可以被定义为密封的,并且只能通过一组固定的case类进行扩展。 正常性状与封闭性状的核心差异可以概括为:

Normal traits are open, so any number of classes can inherit from the trait as long as they provide all the required methods, and instances of those classes can be used interchangeably via the trait's required methods. A normal trait hierarchy makes it easy to add additional sub-classes: just define your class and implement the necessary methods. However, it makes it difficult to add new methods: a new method needs to be added to all existing subclasses, of which there may be many. Sealed traits are closed: they only allow a fixed set of classes to inherit from them, and all inheriting classes must be defined together with the trait itself in the same file or REPL command. A sealed trait hierarchy is the opposite: it is easy to add new methods, since a new method can simply pattern match on each sub-class and decide what it wants to do for each. However, adding new sub-classes is difficult, as you need to go to all existing pattern matches and add the case to handle your new sub-class.

举个例子

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。

A JSON value can only be JSON null, boolean, number, string, array, or dictionary. JSON has not changed in 20 years, so it is unlikely that anyone will need to extend our JSON with additional subclasses. While the set of sub-classes is fixed, the range of operations we may want to do on a JSON blob is unbounded: parse it, serialize it, pretty-print it, minify it, sanitize it, etc. Thus it makes sense to model a JSON data structure as a closed sealed trait hierarchy rather than a normal open trait hierarchy.

  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

‌‌简要:

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

想了解更多细节 Scala中关于密封特征的一切

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

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

例如声明:

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

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