When to prevent class inheritance?

2019-02-13 23:03发布

问题:

I've been told recently a good practice in object oriented programming that you should always allow inheritance from your classes. I really don't think so, but I have no solid arguments on my mind.

Real-world examples of blocked inheritance:

  1. No C++ STL class (specialized class template) allows inheritance (having non-virtual destructors).
  2. Java has its final class modifier that applies to many standard components, like java.lang.String.

Possible reasons I think are:

  1. Security, since subclass might have access to sensitive internals. (I don't think so -- they won't access private members.)
  2. Performance, since a subclass could mess up our efficient implementations by overriding some of the member functions. (Children won't override non-virtual functions.)
  3. To enforce composition over inheritance. (I fully agree. We shouldn't favor inheritance when it's not needed.)

So my question is: In what circumstances should I intentionally block inheritance?

回答1:

In fact, the practice that I try to follow, and that Josh Bloch recommends, in his Effective Java book, is exactly the inverse rule of the one you've been told: Unless you have thought about inheritance, designed your class to be inherited, and documented how your class must be inherited, you should always disable inheritance.

I would recommend reading this chapter of Effective Java (you won't regret buying it), and showing it to the person who told you about this rule.

The most obvious reason to disallow inheritance is immutability. An immutable object is simple to use (only one state), can be cached, shared between many objects, and is inherently thread-safe. If the class is inheritable, anyone can extend the class and make it mutable by adding mutable attributes.



回答2:

Well for starters, only disallow inheritance if you are positive that you don't want others to be extending your class. Preventing inheritance for trivial reasons (such as performance) is usually not recommended, as code reuse often outweighs the small performance gains you can achieve by marking your class final.

That being said, here are several examples when you might want to explicitly prevent inheritance:

  1. You are writing a commercial, closed-source class, and you don't want people to be able to change the functionality down the line. This is a good reason to prevent class inheritance, as you don't want to have to give support for it later on if people have overridden your methods and/or extended your classes, and are complaining that they are getting unexpected results.

  2. You are designing an immutable class. By marking the class final, you are preventing subclasses from compromising the immutable behavior of your class. For example, if you were allowed to subclass String, others could make their own implementations that allows Strings to be modified. Now no code that takes a type String can be certain that the object is immutable.

  3. You want to force composition over inheritance. This is desirable when you want to avoid tight coupling between classes (i.e. you don't want groups of classes that are highly dependent on one another).

  4. You want to encourage inlining by the compiler. Marking classes and methods as final may result in small performance gains, as it will ensure that Java doesn't have to look up the right class method to invoke for an object at runtime. Non-final methods are marked as virtual so that they can be properly extended if needed, final methods can be directly linked or compiled inline in the class. Note that the performance gain you can achieve by doing this is often insignificant (especially if your class' methods are large).



回答3:

Just my 0.02 on this...

Allowing inheritance on a class allows people to deal with unforeseen issues. (ex. the monkeypatching that often occurs in RoR. It can be ugly but it's reality vs. pedantry). Having said that, I'm not a big fan of gratuitous inheritance. The relationship between base and subclasses can be brittle. Deep inheritance hierarchies are hard to grok.

One case I can think of disallowing inheritance is enforcing immutability. This is important for things like the Java String class.