Ted Neward, James Robertson and a few others have been arguing back and forth about the wisdom of preventing derivation of one's classes by marking them as sealed
in C# or final
in Java. Ted covers various reasons why he thinks preventing derivation is valid in posts such as Uh, oh; Smalltalk culture clashes with Java/.NET culture... , Unlearn Bo... and Cedric prefers unsealed classes. On the other side is James Robertson who thinks doing so amounts to excessively trying to protection developers in his post More thoughts on sealed/final.
As a library designer I definitely see why one would want to prevent subclassing of a particular type. My top 3 reasons are
-
Security Considerations: If I have a class that is assumed to perform certain security checks or conform to certain integrity constraints which is depended upon by other classes then it may make sense to prevent inheritance of that class.
-
Fundamental Classes: Certain types are so fundamental to the software system that replacing them is too risky to allow. Types such as strings or numeric types fall into this category. An API that takes a string or int parameter shouldn't have to worry whether someone has implemented their own “optimized string class“ that looks like System.String/java.lang.String but doesn't support Unicode or stores information as UTF-8 instead of UTF-16.
- Focused Points of Extensibility: Sometimes a object model is designed with a certain extensibility points in which case other potential extensibility points should be blocked. For example, in System.Xml in Whidbey we will pointing people to subclass XPathNavigator as their way of exposing data as XML instead of subclassing XmlDocument or XPathDocument. In such cases it would be valid to have made the latter classes final instead of proliferating bad ideas such as XmlDataDocument.
There is a fourth reason which isn't as strong but one I think is still valid
-
Inheritance Testing Cost Prohibitive: A guiding principle for designing subclasses is the Liskov Substitution Principle which states
If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
although straightforward on the surface it is hard work testing that this is indeed the case when designing your base classes. To test that one's classes truly exhibit this principle subclasses should be created and run through a gamut of tests that the base class passes to see if the results are indistinguishable. Often it is actually the case that there is dependency on the internal workings of the class by related components. There are two examples of this in v1.0 of the APIs in System.Xml in the .NET Framework, XmlValidatingReader's constructor accepts an XmlReader as input but really only supports an XmlTextReader and XslTransform should accept an XPathNodeIterator as output from an extension function but really only supports the internal ResetableIterator. Neither of these cases is an example of where the classes should be sealed or final (in fact we explicitly designed the classes to be extensible) but they do show that it is very possible to ship a class where related classes actually have dependencies on its internals thus making subclassing it inappropriate. Testing this can be expensive and making classes final is one way to eliminate an entire category of tests that should be written as part of the QA process of the API. I know more than one team at work who've taken this attitude when designing a class or set of classes.
That's my $0.02 on the topic. I do agree that most developers don't think about the ramifications of making their classes inheritable when designing them but to me that is an argument that final/sealed should not be the default modifier on a class. Most developers won't remember to change the default modifier regardless of what it is, the question is whether it is more beneficial for there to be lots of classes that one can't derive from or that one can even if the class wasn't designed with derivation in mind. I prefer the latter.