ghost
ghost

Reputation: 1217

Why are non case classes allowed to inherit sealed classes?

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

Answers (2)

Tim
Tim

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

Alexey Romanov
Alexey Romanov

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

Related Questions