为什么允许非 case 类继承密封类?

时间:2021-07-19 05:45:10

标签: scala inheritance case-class

我的印象是,密封类主要用于所有详尽的子类都编写在同一个编译单元中(恰好是 Scala 中的文件)。

但是,请考虑以下情况。我有一个名为 AnimalsPart1.scala 的文件,其中有以下声明:

sealed class Animal
case class Dog() extends Animal
case class Cat() extends Animal
class Herbivore() extends Animal

接下来,我有一个名为 AnimalPart2.scala 的文件,其中我有以下声明:

case class Hen() extends Herbivore
def whatAnimalAmI(a : Animal) = a match {
    case Dog() => "Dog"
    case Cat() => "Cat"
    case Hen() => "Hen"
}
println(whatAnimalAmI(new Hen()))

// prints "Hen" as the output

允许类继承密封类,然后让这些类在任何地方都可以继承,违反了密封类的创始原则。这是一个小例子,但如果上述性质成立,则很难为给定的密封类列出详尽的子类集。

因此,为什么允许非 case 类继承密封类?另外,我们如何处理上面的问题(Hen 在声明密封类的文件之外继承了一个 Animal)?

2 个答案:

答案 0 :(得分:3)

如果您想防止 class 被子类化,那么其机制是 final

sealed 类的要点是 classtrait 的所有 直接 子类都在同一个文件中。这允许编译器通过查看是否所有直接子类都匹配来判断 match 是否详尽。

问题中的 match 没有捕获 HerbivoreHerbivore 的其他子类,因此编译器知道它不是详尽无遗的(并且可能会发出警告)。

答案 1 :(得分:1)

<块引用>

允许类继承密封类,然后让这些类在任何地方都可以继承,违背了密封类的创始原则

在这种情况下,原则是与直接子类型(以及类本身,如果它不是抽象的)匹配是详尽无遗的

def whatAnimalAmI(a : Animal) = a match {
    case Dog() => "Dog"
    case Cat() => "Cat"
    case h: Herbivore => "Herbivore"
}

这样你就不需要查看任何其他文件并发现 Herbivore 有一些子类。所以这个原则并没有被打破。

现在,您确实也可以进行不穷尽的匹配,但是即使 Herbivore 是一个案例类(或密封的),您也可以通过省略它来做到这一点。