Why is there an option for a class in OOP that needs to be checked in order to never inherit from ancestors?

"sealed" keyword in C #, Final in Java.

As I almost never create a diagram, and I use only the classes that I have already made (from frameworks), I still donโ€™t know after some years why someone is โ€œblockingโ€ the class, so it will never be extended / inherited.

This is useful? Are there any harm that allows to extend all classes from inheritance, rejecting the ability to "seal"?

Sorry to ask the question in 2012 when the OOP is trivial, but I would like a good explanation and / or a good source to read! Because for me it is useless, and I canโ€™t believe that this is just a concept.

But everywhere I seek the same answer: "mark the class so that it is not inherited from others."

+4
inheritance oop
Sep 25 '12 at 17:32
source share
2 answers
  • Safety An inheriting class may have access to the internal parts of the base class, breaking encapsulation

  • Saving the contract - the inheriting class can break the contract provided by the base class. See Liskov replacement principle.

  • Immutability - special case 2. - the inheritance class can introduce mutable fields into immutable classes

  • Performance - a virtual machine can aggressively optimize such classes, for example. assume methods are never overridden avoiding virtual calls

  • Simplicity - implementing methods like equals() is much simpler without worrying about inheritance

+5
Sep 25
source share

The reason is that if you allow a subclass and do not set restrictions on which methods the subclass can override, updates to the base class that preserve its behavior can still subclass subclasses. In other words, it is unsafe to inherit from a class that is not specifically designed for extension (by assigning certain methods as redefinable and creating all the rest final .) This is called a fragile base class problem - see this document for an example and this document for a more thorough analysis of the problem .

If the base class was not intended to be inherited and is part of a public API (possibly a library), you now have serious problems. You have no control over who subclasses your code, and you cannot find out if the change will be safe for subclasses simply by looking at the base class. The bottom line is that you either develop unlimited inheritance, or completely abandon it.




Note that it is possible to have a finite number of subclasses. This can be achieved using a private constructor and inner classes:

 class Base { private Base() {} (public|private) static final class SubA extends Base { ... } (public|private) static final class SubB extends Base { ... } (public|private) static final class SubC extends Base { ... } } 

Inner classes have access to a private constructor, but top-level classes do not, so you can subclass it only internally. This allows us to express the view that "a Base value may be SubA OR SubB OR SubC." There is no danger here, because Base will usually be empty (you don't actually inherit anything from Base), and all subclasses are under your control.

You might think that this is the smell of code, as you know the types of subclasses. But you must understand that this alternative way of implementing abstractions complements the interfaces. When you have a finite number of subclasses, it is easy to add new functions (handle a fixed number of subclasses), but it is difficult to add new types (every existing method needs to be updated to handle a new subclass). When you use the interface or allow unlimited subclasses, it is easy to add a new type (implement a fixed number of methods), but it is difficult to add new methods (you need to update each class). One of the strengths is another weakness. For a more detailed discussion of this topic, see About Understanding Data Abstraction, Revised . EDIT: My mistake; the document I linked says about another duality (ADTs vs objects / interfaces). Actually, I was thinking about the problem of expression .




There are less serious reasons to avoid inheritance. Suppose you start with class A and then implement a new function in subclass B, and then add another function to subclass C. Then you realize that you need both functions, but you cannot create a subclass BC that extends as B , and C. This can be bitten even if you create for inheritance and prevent the problem of a fragile base class. In addition to the pattern that I showed above, most inheritance applications are best replaced with composition โ€” for example, using the Strategy Pattern (or just using high-order functions if your language supports it.)

+2
Jan 14 '14 at 15:12
source share



All Articles