1

What is the point of sealing a class? Why should we seal a class at all? I've found a bit contradicting answers on the internet, for example here: https://stackoverflow.com/questions/268251/why-seal-a-class
It is firstly written that every class that is not to be inherited, should be sealed, and then that it is not a good practice. They say that it has changed over the years, but again even if that is the case, I'm interested to hear what the practice is now and why. Or maybe it has become, or even has always been, just a matter of preference of a programmer?

8
  • 1
    "Why should we seal a class at all?" Because we don't want it to be inherited or extended, and didn't implement it with that intend? – πάντα ῥεῖ Sep 2 '20 at 16:20
  • But what is the difference between doing that and leaving it just as it is without the word 'sealed' next to it? – frgt Sep 2 '20 at 16:25
  • sealed will prevent inheritance. – πάντα ῥεῖ Sep 2 '20 at 16:26
  • 3
    Yes, exactly that's the purpose. – πάντα ῥεῖ Sep 2 '20 at 16:31
  • 3
    There is a reason why Jon Skeet's answer got more than 100 points. I recommend to trust him, he knows what he is talking of. – Doc Brown Sep 2 '20 at 20:02
10

If the class is just for use in your own code, or in code maintained by a small team that you're part of, then it maybe doesn't matter so much whether the class is sealed or not.

Where it really matters is if the class is part of a published interface, and you want to use your class to help other developers writing code that you can't edit, and who you may not even know exist.

In that case, if the class is not sealed and someone outside your team extends it, the first you may hear about it is when you later make a change to the design of the class and they update their copy of your code and complain that it broke their extended class. Or they don't complain but they just stop using your code. Or you anticipate the complaint, and avoid making those changes, which makes it harder to implement new features.

If the class is sealed they won't be able to extend it in the first place, and may choose a more robust mechanism like object composition instead. If they really want to unseal the class they might take a copy of your source code and delete the word sealed, or perhaps use reflection or something to circumvent the sealing. A programmer doing either of those things should know not to expect any guarantees that the code will work, especially when they update to a new version of your class. Setting expectations is important.

6
  • Sometimes extending a class can be used to work around library bugs, and users of the library may be frustrated that the library author blocked them from working around their bug. – user253751 Sep 3 '20 at 19:43
  • If the library is open source users can fix the bug in their own copy. In PHP the keyword is 'final' instead of 'sealed', and this issue was debated between some prominent developers recently: see twitter.com/nicolasgrekas/status/1237055492272332801 – bdsl Sep 3 '20 at 21:22
  • 1
    "may choose a more robust mechanism like object composition instead" - frequently a much better approach - if only languages provided good support for composition via semantics, syntactic sugar, or whatever ... - problem is that when the only built-in tool you've got is a hammer, everything looks like a nail. – davidbak Sep 3 '20 at 22:16
  • @davidbak fun fact: C++ has that, it's called "inheritance" as long as you don't define any overrideable methods. – user253751 Sep 4 '20 at 13:59
  • @user253751 - inheritance != composition, sorry; many articles discussing this, e.g., en.wikipedia.org/wiki/Composition_over_inheritance – davidbak Sep 4 '20 at 15:29
8

What’s the difference: The difference is that if the class isn’t sealed but should be, in six or twelve months someone will extend it in some inappropriate way, introducing subtle bugs that take weeks to find an fix, all because you didn’t seal it.

“I can’t think of a scenario where it should be extended” is irrelevant. What’s relevant is that right now, as it is, it can’t be successfully extended, and that you are not paid to do the extra work to make it extendible. If there is a good reason why someone would want to extend it at some point in the future, then you invest the time and work to make it extendible, and then you unseal it.

0

Sealing a class can have many different purposes.

In Scala, a sealed class or trait can only be inherited from by templates defined in the same source file. This means that inheritance can be tightly controlled by the designer of the inheritance hierarchy, in particular, the designer can ensure that only a strictly defined set of templates can extend the class or trait.

It also allows the compiler to perform exhaustiveness checks for Pattern Matching.

This can be used to model Closed Algebraic Sum Types using Inheritance, without the language needing to have explicit support for Algebraic Data Types. Every Constructor simply becomes a subclass of the sealed class or trait.

For example, in a language with Algebraic Sum Types, you would model a list something like this (example in Haskell):

data List a = Nil | Cons a (List a)

In a programming language without Algebraic Sum Types, you can model this using Inheritance: the type becomes the top class, the data constructors become the subclasses (example in Java):

interface List<A> {}
record Nil() implements List<Object> {}
record Cons<A>(A head, List<A> tail) implements List<A> {}

But that is unsatisfactory: everybody can just add more subclasses, but that doesn't make sense: a list is either empty or non-empty, there is no third case, so there should never be a third subclass.

By sealing the top class, we can prevent that from happening (example in Scala):

abstract sealed trait List[+A]
case object Nil extends List[Nothing]
final case class ::[+A](head: A, tail: List[A]) extends List[A]

Now, we can be sure that there will never be another subclass of List[+A]: there cannot be a direct subclass of it because it is sealed and there cannot be an indirect subclass of it because the set of possible subclasses is closed (Nil and ::[+A]) and all classes in this set are final (note that objects are implicitly final).

This is, in fact, mostly how lists are implemented in the Scala standard library, modulo some efficiency concerns for real-world usage:

0

A point against sealing classes:

Even if you don't want to enable inheritance, you still want to enable inheritance (most of the time). Why? Because you want to enable a mocking framework to replace instances of your "sealed" class with a mock object. Especially for unit tests you really want only the one class you're testing at the moment to be "active" so to speak and all its dependent objects to be mocks. Otherwise your really doing something more akin to a component/system/integration test and not a unit test. Of course, that is only possible if you actually can mock the "sealed" class. Not every class has an interface; so many mocking frameworks create mocks by subclassing on the fly. That is not possible if you forbid subclassing.

(Of course, there may or may not be ways around this with reflection / code-manipulation or -creation at runtime / classloader shenanigans etc. depending on the language and the mocking frameworks)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.