密封类在“在Scala中编程”中进行了描述,但是密封性没有。
在哪里可以找到有关密封性的更多信息?

我想知道,如果密封性是
如果不是,则有什么区别?
什么时候使用密封特性(什么时候不使用)是一个好主意?

#1 楼

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


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

评论


我花了六个月的时间才能随机到达这里,并了解如何在Scala中替换Java Enum。

–sscarduzio
2014年10月14日14:58

非常好 !并且不仅是有限且事先已知的,而且是受限(密封的)上下文的一部分,在该上下文中可以检查所有可能的子类型,例如yes |不,甚至奇怪的...

–圣维拉岛(MáriodeSáVera)
18-4-7在16:13



#2 楼


密封特征与密封类相同吗?


sealed而言,是的。当然,它们在traitclass之间具有正常的区别。


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


模拟。


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


如果您有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]


因为上下文的边界(和视图边界)是使用隐式参数实现的。鉴于traits无法接收参数,您不能这样做。

我个人更喜欢sealed trait并使用它,除非某些特殊原因使我使用sealed abstract class。而且我并不是在说些微妙的原因,而是在谈论您无法忽略的表面原因,例如使用类型类。

评论


“因为上下文边界(和视图边界)是使用隐式参数实现的。” -您能详细说明吗?

–艾琳娜·拉波特(Irina Rapoport)
17-10-8在17:48

@Ruby –回复很晚,但是如果您或其他任何人感兴趣:上下文边界([A:F])的工作方式与方差约束不同。相反,它是语法糖,要求范围内的隐式F [A]。通常用于召唤类型类实例的方式比隐式参数((implicit fa:F [A]))有点更易读,但仍在幕后运作,就像Daniel指出的那样。出来,特质就没有做到这一点。

– mirichan
19-09-30在18:05

#3 楼

来自Daily-scala博客:


当特征被“密封”时,其所有子类都在同一文件中声明,这使得子集的集合变得有限,从而允许
某些编译器检查。


评论


谢谢。 “所有子类”意味着类和特征?

–约翰·特里普伍德
2012年6月26日上午8:27

@John-我还没尝试过,但我怀疑是在上课。关于密封的要点是,一切都在一个源单元内定义

–布赖恩·阿格纽(Brian Agnew)
2012年6月26日上午8:37

@JohnThreepwood:类,特征和对象。大多数时候,在Scala中,术语“类”用于指代类,特征和对象。仅在谈论它们之间的特定差异时,它仅表示类。 SLS使用术语“模板”来指代类和特征,但是在SLS之外并没有太多使用,并且没有包含所有三个类,特征和对象的术语。

–Jörg W Mittag
2014年12月17日下午16:05

#4 楼

同样,我觉得有必要向您指出规格:密封修饰符适用于类定义。密封类不能直接继承,除非继承模板在与继承类相同的source
文件中定义。但是,密封类的子类可以在任何地方继承。

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


#5 楼

‌‌简而言之:


密封的特征只能在同一文件中扩展
列出,这使编译器可以轻松地知道所有可能的子类型
当可能的数量很多时使用密封的特征子类型是有限的,并且事先已知。
在Java中创建类似枚举的方法
帮助定义代数数据类型(ADT)

以及更多详细信息
关于Scala中的密封特征

#6 楼

特质也可以定义为封闭的,并且只能通过固定的一组case classes进行扩展。
正常性状和封闭性状之间的核心差异可以归纳为:


正常性状是开放的,因此只要提供了所有必需的方法,任何数量的类都可以从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

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