The main reason to seal classes seems to be that this allows the compiler to do exthaustivity searches when pattern matching on those classes. Say I have data types meant for pattern matching. Toy example:
sealed trait Statement
case class Assign(name: String, value: Int) extends Statement
case class Print(name: String) extends Statement
case class IfZero(name: String, thenn: Statement, els: Option[Statement]) extends Statement
case class Block(statements: List[Statement]) extends Statement
The use case for these classes would be to consume them through pattern matching:
def execute(statement: Statement): Unit = statement match {
case Assign(name, value) => ???
case Print(name) => ???
case IfZero(name, thenn, els) => ???
case Block(statements) => statements foreach { execute(_) }
}
To this end, the Statement
trait is sealed
so that the compiler can warn me if I forget one statement kind in the match statement. But what about the case classes? Case classes cannot inherit from each other, but traits and ordinary classes can. So, is it good practice to seal the case classes as well? What could go wrong if I don't?
1. We use sealed classes to prevent inheritance. As we cannot inherit from a sealed class, the methods in the sealed class cannot be manipulated from other classes. It helps to prevent security issues.
Sealed traits are closed: they only allow a fixed set of classes to inherit from them, and all inheriting classes must be defined together with the trait itself in the same file or REPL command.
Definition. The sealed is a Scala keyword used to control the places where given trait or class can be extended. More concretely, the subclasses and the implementations can be defined only in the same source file as the sealed trait or class.
The answer is simple: Case Class can extend another Class, trait or Abstract Class. Create an abstract class which encapsulates the common behavior used by all the classes inheriting the abstract class.
You don't have to seal the case classes but you should mark them as final
and therefore forbid any further inheritance relationship. Making them sealed is only useful when you want exhaustiveness checking on its subclasses, which is not a very likely use case.
Marking all classes as final
by default is a good thing because it forbids the users of your API to change the behavior of these classes when they override its methods. If you didn't specifically design your class to be subclassed, it may happen that the subclassing leads to bugs in your application because the subclassed class is no longer doing what it was intended to do.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With