Sunday, September 8, 2024
HomeJavaDesign and doc for inheritance—or else prohibit it. Right here’s how.

Design and doc for inheritance—or else prohibit it. Right here’s how.


It’s harmful to subclass a “international” class that was not designed and documented for inheritance.

Oracle Java, Java Exam Prep, Oracle Java Career, Java Skills, Java Jobs, Java Tutorial and Materials

In different phrases, a subclass depends upon the implementation particulars of its superclass for its correct operate. The superclass’s implementation could change from launch to launch, and if it does, the subclass could break, regardless that its code has not been touched.

That begs the query: What does it imply for a category to be designed and documented for inheritance?

First, doc

A category designed and documented for inheritance should exactly doc the results of overriding any technique. In different phrases, the category should doc its self-use of overridable strategies.

For every public or protected technique, the documentation should point out which overridable strategies the strategy invokes, in what sequence, and the way the outcomes of every invocation have an effect on subsequent processing. (The time period overridable means a technique is nonfinal and both public or protected.)

Extra usually, a category should doc any circumstances underneath which it’d invoke an overridable technique. For instance, invocations may come from background threads or static initializers.

A technique that invokes overridable strategies incorporates an outline of those invocations on the finish of its documentation remark. The outline is in a particular part of the specification, labeled “Implementation Necessities,” which is generated by the @implSpec Javadoc tag. This part describes the internal workings of the strategy. The next is an instance copied from the java.util.AbstractCollection specification:

public boolean take away(Object o)

Removes a single occasion of the required factor from this assortment, whether it is current (elective operation). Extra formally, removes a component e such that Objects.equals(o, e), if this assortment incorporates a number of such parts. Returns true if this assortment contained the required factor (or equivalently, if this assortment modified on account of the decision)…

Implementation Necessities: This implementation iterates over the gathering in search of the required factor. If it finds the factor, it removes the factor from the gathering utilizing the iterator’s take away technique. Observe that this implementation throws an UnsupportedOperationException if the iterator returned by this assortment’s iterator technique doesn’t implement the take away technique and this assortment incorporates the required object.

The specification documentation leaves little question that overriding the iterator technique will have an effect on the conduct of the take away technique. The documentation additionally describes precisely how the conduct of the Iterator returned by the iterator technique will have an effect on the conduct of the take away technique. Distinction this to the state of affairs described within the article “It is best to favor composition over inheritance in Java. Right here’s why.” In that article, the programmer subclassing HashSet merely couldn’t say whether or not overriding the add technique would have an effect on the conduct of the addAll technique.

Wait: Doesn’t this violate the dictum that good API documentation ought to describe what a given technique does and never the way it does it? Sure; it does! That is an unlucky consequence of the truth that inheritance violates encapsulation. To doc a category in order that it may be safely subclassed, it’s essential to describe implementation particulars that ought to in any other case be left unspecified.

By the best way, the @implSpec tag was added in Java 8 and is used closely in Java 9. This tag needs to be enabled by default, however as of Java 9, Javadoc ignores the tag until you move the command line swap -tag “implSpec:a:Implementation Necessities:”.

Second, design

Designing for inheritance includes extra than simply documenting patterns of self-use. To permit programmers to write down environment friendly subclasses with out undue ache, a category could have to supply hooks into its inner workings within the type of judiciously chosen protected strategies or, in uncommon situations, protected fields.

For instance, think about the next documentation for the removeRange technique from the java.util.AbstractList specification:

protected void removeRange(int fromIndex, int toIndex)

Removes from this record all the parts whose index is between fromIndex, inclusive, and toIndex, unique. Shifts any succeeding parts to the left (reduces their index). This name shortens the record by (toIndex – fromIndex) parts. (If toIndex == fromIndex, this operation has no impact.)

This technique is named by the clear operation on this record and its sublists. Overriding this technique to benefit from the internals of the record implementation can considerably enhance the efficiency of the clear operation on this record and its sublists.

Implementation Necessities: This implementation will get a listing iterator positioned earlier than fromIndex and repeatedly calls ListIterator.subsequent adopted by ListIterator.take away, till the complete vary has been eliminated. Observe: If ListIterator.take away requires linear time, this implementation requires quadratic time.

Parameters:

fromIndex – index of first factor to be eliminated.

toIndex – index after final factor to be eliminated.

The removeRange technique is of no curiosity to finish customers of a Listing implementation; it’s offered solely to make it straightforward for subclasses to supply a quick clear technique on sublists. Within the absence of the removeRange technique, subclasses must make do with quadratic efficiency when the clear technique was invoked on sublists or rewrite the complete subList mechanism from scratch—not a straightforward activity!

How do you resolve which protected members to reveal whenever you design a category for inheritance? Sadly, there isn’t a magic bullet. The perfect you are able to do is to assume laborious, take your finest guess, after which take a look at it by writing subclasses. It is best to expose as few protected members as doable as a result of every one represents a dedication to an implementation element. Alternatively, it’s essential to not expose too few as a result of a lacking protected member can render a category virtually unusable for inheritance.

Write subclasses

The one option to take a look at a category designed for inheritance is to write down subclasses. In case you omit an important protected member, attempting to write down a subclass will make the omission painfully apparent. Conversely, if a number of subclasses are written and none makes use of a protected member, you must in all probability make the category personal. Expertise exhibits that three subclasses are often adequate to check an extendable class. A number of of those subclasses needs to be written by somebody apart from the superclass creator.

While you design for inheritance a category that’s more likely to obtain large use, understand that you’re committing eternally to the self-use patterns that you simply doc and to the implementation choices implicit in its protected strategies and fields. These commitments could make it tough or unattainable to enhance the efficiency or performance of the category in a subsequent launch. Due to this fact, it’s essential to take a look at your class by writing subclasses earlier than you launch it.

Additionally, word that the particular documentation required for inheritance clutters up regular documentation, which is designed for programmers who create situations of your class and invoke strategies on them.

Class restrictions

There are a couple of extra restrictions {that a} class should obey to permit inheritance.

Constructors should not invoke overridable strategies, instantly or not directly. In case you violate this rule, program failure will end result. The superclass constructor runs earlier than the subclass constructor, so the overriding technique within the subclass will get invoked earlier than the subclass constructor has run. If the overriding technique depends upon any initialization carried out by the subclass constructor, the strategy won’t behave as anticipated. To make this concrete, the next is a category that violates this rule:

public class Tremendous {

    // Damaged – constructor invokes an overridable technique

    public Tremendous() {

        overrideMe();

    }

    public void overrideMe() {

    }

}

Right here’s a subclass that overrides the overrideMe technique, which is erroneously invoked by the Tremendous class’s sole constructor.

public closing class Sub extends Tremendous {

    // Clean closing, set by constructor

    personal closing Prompt on the spot;

    Sub() {

        on the spot = Prompt.now();

    }

    // Overriding technique invoked by superclass constructor

    @Override public void overrideMe() {

           System.out.println(on the spot);

    }

    public static void primary(String[] args) {

        Sub sub = new Sub();

        sub.overrideMe();

    }

}

You may anticipate this program to print out the worth of Prompt twice—however as an alternative it prints null the primary time as a result of overrideMe is invoked by the Tremendous constructor earlier than the Sub constructor has an opportunity to initialize the moment discipline.

Observe that this program observes a closing discipline in two totally different states!

Observe additionally that if overrideMe had invoked any technique on on the spot, it will have thrown a NullPointerException when the Tremendous constructor invoked overrideMe. The one motive this program doesn’t throw a NullPointerException because it stands is that the println technique tolerates null parameters.

By the best way, it’s secure to invoke personal strategies, closing strategies, and static strategies, none of that are overridable, from a constructor.

The Cloneable and Serializable interfaces current particular difficulties when you’re designing for inheritance. It’s usually not a good suggestion for a category designed for inheritance to implement both of those interfaces as a result of they place a considerable burden on programmers who prolong the category. (There are, nevertheless, particular actions which you can take to permit subclasses to implement these interfaces with out mandating that they accomplish that, however that’s past the scope of this text.)

In case you do resolve to implement both Cloneable or Serializable in a category that’s designed for inheritance, try to be conscious that as a result of the clone and readObject strategies behave quite a bit like constructors, one other restriction applies: Neither clone nor readObject could invoke an overridable technique, instantly or not directly.

Within the case of readObject, the overriding technique will run earlier than the subclass’s state has been deserialized. Within the case of clone, the overriding technique will run earlier than the subclass’s clone technique has an opportunity to repair the clone’s state. In both case, a program failure is more likely to observe. Within the case of clone, the failure can injury the unique object in addition to the clone! This will occur, for instance, if the overriding technique assumes it’s modifying the clone’s copy of the article’s deep construction, however the copy hasn’t truly been made but.

Essential word: In case you resolve to implement Serializable in a category designed for inheritance and the category has a readResolve or writeReplace technique, it’s essential to make these strategies protected quite than personal. If these strategies are personal, they are going to be silently ignored by subclasses. That is another case the place an implementation element turns into a part of a category’s API to allow inheritance.

Prohibit subclassing the place it’s unsafe

By now it needs to be obvious that designing a category for inheritance requires nice effort and locations substantial limitations on the category. Designing for inheritance shouldn’t be a call to be undertaken flippantly.

Nonetheless, there are some conditions the place it’s clearly the best factor to do, corresponding to for summary lessons, together with skeletal implementations of interfaces. There are different conditions the place it’s clearly the improper factor to do, corresponding to for immutable lessons.

However what about unusual concrete lessons? Historically, they’re neither closing nor designed and documented for subclassing, however this state of affairs is harmful. Every time a change is made in such a category, there’s a probability that subclasses extending the category will break. This isn’t only a theoretical drawback. It’s not unusual to obtain subclassing-related bug stories after modifying the internals of a nonfinal concrete class that was not designed and documented for inheritance.

The perfect answer to this drawback is to ban subclassing in lessons that aren’t designed and documented to be safely subclassed. There are two methods to ban subclassing. The simpler of the 2 is to declare the category closing. The choice is to make all of the constructors personal or package-private and so as to add public static factories instead of the constructors. Both strategy is appropriate.

This recommendation could also be considerably controversial as a result of many programmers have grown accustomed to subclassing unusual concrete lessons so as to add services—corresponding to instrumentation, notification, and synchronization—or to restrict performance. If a category implements some interface that captures its essence, corresponding to Set, Listing, or Map, you must really feel no compunction about prohibiting subclassing. Utilizing a wrapper class offers a superior different to inheritance for augmenting the performance.

If a concrete class doesn’t implement an ordinary interface, you might inconvenience some programmers by prohibiting inheritance. In case you really feel that it’s essential to enable inheritance from such a category, one affordable strategy is to make sure that the category by no means invokes any of its overridable strategies and to doc this truth. In different phrases, eradicate the category’s self-use of overridable strategies completely. In doing so, you’ll create a category that’s moderately secure to subclass. Overriding a technique won’t ever have an effect on the conduct of every other technique.

You’ll be able to eradicate a category’s self-use of overridable strategies mechanically, with out altering its conduct. To take action, transfer the physique of every overridable technique to a personal “helper technique” and have every overridable technique invoke its personal helper technique. Then substitute every self-use of an overridable technique with a direct invocation of the overridable technique’s personal helper technique.

Supply: oracle.com

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments