什么是密封特性?

Joh*_*ood 322 scala traits sealed

"Scala编程"中描述了密封类,但密封特性不是.我在哪里可以找到有关密封特性的更多信息?

我想知道,如果密封的特性与密封的类相同吗?或者,如果没有,有什么区别?何时使用密封特性(何时不是)?

par*_*tic 453

一个sealed特质只能在同一个文件作为其声明中被延长.

它们通常用于提供替代品enums.由于它们只能在单个文件中扩展,因此编译器知道每个可能的子类型并且可以对其进行推理.

例如声明:

sealed trait Answer
case object Yes extends Answer
case object No extends Answer
Run Code Online (Sandbox Code Playgroud)

如果匹配不详尽,编译器将发出警告:

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
Run Code Online (Sandbox Code Playgroud)

因此,如果可能的子类型数量是有限的并且事先已知,则应使用密封特征(或密封的抽象类).有关更多示例,您可以查看列表选项实现.

  • 我花了六个月的时间来到这里,并了解如何在Scala中替换Java Enum. (106认同)
  • Scala3终于得到了枚举https://alvinalexander.com/scala/example-enums-in-scala-3-dotty-match-expression/ (3认同)

Dan*_*ral 88

密封的特性和密封的一样?

至于sealed去,是的.它们共享之间的正常差异traitclass,当然.

或者,如果没有,有什么区别?

没有实际意义.

何时使用密封特性(何时不是)?

如果你有sealed class X,那么你必须检查X以及任何子类.同样不是sealed abstract class X或者sealed trait X.所以你可以这么做sealed abstract class X,但这样做比单纯的更加冗长,而且trait收效甚微.

使用abstract classover a 的主要优点trait是它可以接收参数.使用类型类时,这一优势尤为重要.例如,假设您要构建一个排序树.你可以这样写:

sealed abstract class Tree[T : Ordering]
Run Code Online (Sandbox Code Playgroud)

但你不能这样做:

sealed trait Tree[T : Ordering]
Run Code Online (Sandbox Code Playgroud)

因为上下文边界(和视图边界)是用隐式参数实现的.鉴于特征无法接收参数,您无法做到这一点.

就个人而言,我更喜欢sealed trait并使用它,除非某些特殊原因让我使用它sealed abstract class.我不是在谈论微妙的原因,而是在面对面的原因,你不能忽视,比如使用类型类.


Bri*_*new 52

来自每日scala博客:

当特征被"密封"时,它的所有子类都在同一个文件中声明,这使得子类集合有限,允许某些编译器检查.


A T*_*A T 27

另外我觉得有必要指出你的规格:

密封 MODI网络呃适用于类去连接nitions.甲密封类可以不直接继承,但如果继承模板是德音响相同的源文件作为继承的类中定义.但是,密封类的子类可以在任何地方继承.

- M. Odersky.Scala语言规范,版本2.8.在线,2013年9月.


Maj*_*ini 9

??简要地:

  • Sealed trait 只能在同一个文件中扩展
  • 列出这让编译器很容易知道所有可能的子类型
  • 当可能的子类型数量有限且事先已知时,使用密封特征
  • 在 Java 中创建类似 enum 的方法
  • 帮助定义代数数据类型 (ADT)

以及更多细节 关于Scala中密封特征的一切


Che*_*ema 5

特征也可以被定义为密封的,并且仅通过一组固定的扩展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
Run Code Online (Sandbox Code Playgroud)

一般来说,密封特征非常适合对层次结构进行建模,在这种层次结构中,您预计子类的数量变化很小或根本不变化。可以使用密封特征进行建模的一个很好的例子是 JSON

  • JSON只能是JSONnull、布尔值、数字、字符串、数组或字典。
  • JSON20 年来没有改变,所以不太可能有人需要JSON 用额外的子类来扩展我们的。
  • 虽然子类集是固定的,但我们可能想要对JSONblob 执行的操作范围是无限的:解析它、序列化它、漂亮打印它、缩小它、清理它等等。因此建模是有意义的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
Run Code Online (Sandbox Code Playgroud)