Reputation: 1217
I was under the impression that sealed classes mostly serve the purpose that all exhaustive subclasses are written in the same compilation unit (which happens to be a file in Scala).
However, think of the following situation. I've a file called AnimalsPart1.scala, wherein I have the following declarations:
sealed class Animal
case class Dog() extends Animal
case class Cat() extends Animal
class Herbivore() extends Animal
Next, I've a file called AnimalPart2.scala wherein I have the following declaration:
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
Allowing classes to inherit sealed classes, and then making those classes inheritable everywhere breaks the founding principle of sealed classes. This is a small example, but if the above property holds true, it would be very difficult to list exhaustive set of subclasses for a given sealed class.
Why, therefore, is a non case class allowed to inherit sealed class? Also, how do we deal with the above problem (Hen inheriting an Animal outside the file where the sealed class was declared)?
Upvotes: 1
Views: 255
Reputation: 27421
If you want to prevent a class
from being subclassed then the mechanism for that is final
.
The point of a sealed
class is that all the direct subclasses of a class
or trait
are in the same file. This allows the compiler to tell if a match
is exhaustive by seeing if all the direct subclasses are matched.
The match
in the question does not catch Herbivore
or other subclasses of Herbivore
so the compiler knows that it is not exhaustive (and may give a warning).
Upvotes: 3
Reputation: 170879
Allowing classes to inherit sealed classes, and then making those classes inheritable everywhere breaks the founding principle of sealed classes
The principle is that matching against direct subtypes (and the class itself if it isn't abstract) is exhaustive, in this case
def whatAnimalAmI(a : Animal) = a match {
case Dog() => "Dog"
case Cat() => "Cat"
case h: Herbivore => "Herbivore"
}
This way you don't need to look at any other files and find that Herbivore
has some subclasses. So this principle isn't broken.
Now, it's true that you can match inexhaustively too, but you could do that even if Herbivore
were a case class (or sealed) by omitting it.
Upvotes: 1