Good Reasons to Prohibit Inheritance in Java

Good reasons to prohibit inheritance in Java?

Your best reference here is Item 19 of Joshua Bloch's excellent book "Effective Java", called "Design and document for inheritance or else prohibit it". (It's item 17 in the second edition and item 15 in the first edition.) You should really read it, but I'll summarize.

The interaction of inherited classes with their parents can be surprising and unpredictable if the ancestor wasn't designed to be inherited from.

Classes should therefore come in two kinds:

  1. Classes designed to be **extended**, and with enough documentation to describe how it should be done

  2. Classes marked **final**

If you are writing purely internal code, this may be a bit of overkill. However, the extra effort involved in adding five characters to a class file is very small. If you are writing only for internal consumption, then a future coder can always remove the 'final' - you can think of it as a warning saying "this class was not designed with inheritance in mind".

When to prevent class inheritance?

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.

Does inheritance cause waste of space?

The JVM specification page 570 implies that the answer to your question is "no"; a virtual method that is not overriden will use the superclass's version of that method directly, rather than make a copy of it for its own use.

See this previous answer for more info on vtable dispatch.

Why use inheritance at all?

If you delegate everything that you haven't explicitly overridden to some other object implementing the same interface (the "base" object), then you've basically Greenspunned inheritance on top of composition, but (in most languages) with a lot more verbosity and boilerplate. The purpose of using composition instead of inheritance is so that you can only delegate the behaviors you want to delegate.

If you want the object to use all the behavior of the base class unless explicitly overridden, then inheritance is the simplest, least verbose, most straightforward way to express it.

prevent inheritance of interface outside of package

From the java tutorial:

.. All abstract, default, and static methods in an interface are implicitly public, so you can omit the public modifier.

It means that you can't unless you restrict the interface visibility to package. But i guess that you can't.

package foo;

interface Foo
{
}

I suppose that you could write a custom annotation (somelink like @InstanciableOnlyInMyPackage) and put it on the interface. And then using raise a compiler error using Annotation Processing Tool.

Keeping a value class non-final for possible future extensibility

In my opinion, it is good practice to make simple value types final. If you want to guarantee immutability, you actually have to do so. That's also (partially) why String, Integer, etc are all final.

If your class is not final, somebody could extend it by adding methods that mutate it. A client who is passed an instance of the extended type (upcasted to your type) would falsely believe to deal with an immutable object when it actually isn't.

In my own code, I actually go a little further and make almost any class final if I didn't design it with extensibility explicitly in mind. If you want to support extension, consider providing an abstract class or an interface. This is also in line with the abstract, final or empty rule for methods.

Update: Why does immutability require a class to be final? Of course, there are other ways to ensure a particular attribute of an object is not changed.

Consider for example a RGBColor class with three attributes red, green and blue of type byte. We make all three final and set them in the constructor once for all time. (We can additionally make them private and add appropriate getter methods but that's not important for the sake of this discussion.) Of course, we override the equals method to return true if and only if the compared object is an instance of RGBColor with the same red, green and blue values.

This looks innocent but what if somebody decides to extend our class to a RGBAColor class by adding an alpha attribute? Naturally, the extender would desire to override equals to also take into account the alpha value. Suppose our extender also isn't very careful about immutability and thus makes alpha non-final and supplies a setter for it.

Now, if we are given an object of type RGBColor, we cannot safely assume that if it compared equal to another one, it will still do so a minute from now. We could have prevented this (particular problem) by also declaring equals as final in our RGBColor class. But then, we could have equally well made the entire class final because extending a value type without the possibility to extend the notion of equality is close to useless. (Thre are other problems with overriding equals such as it not being symmetric. I generally feel not too comfortable about it.)



Related Topics



Leave a reply



Submit