Effective Java Third edition--19. If you use inheritance then design and document the description, otherwise you should not use

Source: Internet
Author: User

Tips
"Effective Java, third Edition" an English version has been published, the second edition of this book presumably many people have read, known as one of the four major Java books, but the second edition of 2009 published, to now nearly 8 years, but with Java 6, 7, 8, and even 9 of the release, the Java language has undergone profound changes.
In the first time here translated into Chinese version. For everyone to learn to share.

19. If the use of inheritance is designed and documented, otherwise it should not be used

Entry 18 reminds you of the danger of inheriting the subclass of the "Alien" class that has no design and documentation descriptions. So what does it mean to design and document A class for inheritance?

First, this class must accurately describe the impact of overriding this method. In other words, the class must document the self-use of Overridable methods (Self-use). For each public or protected method, the document must indicate which override methods are called by the method, in what order, and how the results of each invocation affect subsequent processing. (overriding method, here refers to a method that is not final decorated, whether public or protected.) More generally, a class must document any cases where an overridable method might be called. For example, a background thread or a static initialization code block might call such a method.

The

method that invokes an overridable method contains a description of the calls at the end of the document comment. These are described in a specific part of the specification, labeled "Implementation Requirements," and generated by the Javadoc label @implSpec . This section describes how the method works internally. The following is an example of a copy from the specification of the Java.util.AbstractCollection class:

  Public boolean remove (Object o) removes a single instance of the specified element from this collection, if it I s present (optional operation).  More formally, removes a element e such that objects.equals (O, E), if the collection contains one or more such elements. Returns true if this collection contained the specified element (or equivalently, if this collection changed as a result of the call). Implementation Requirements:this implementation iterates over the collection looking for the specified element. If it finds the element, it removes the element from the collection using the iterator ' s remove method. Note that this implementation throws a unsupportedoperationexception if the iterator returned by this collection ' s Iterat or method does not implement the Remove method and this collection contains the specified object.  

Removes a single instance of the specified element (if present, an optional instance operation) from the collection. More formally, if this collection contains one or more such elements, the deletion makes Objects.equals(o, e) an element E. Returns True if this collection contains the specified element (or the equivalent of this collection has changed because of a call).

Implementation Requirements : This implementation iterates through the collection to find the specified element. If an element is found, the iterator's method is used to remove remove the element from the collection. Note that this implementation throws an exception if the method returned by this collection's iterator methods does not implement the remove method, and this collection contains the specified object UnsupportedOperationException .

This document, without a doubt, shows that the overriding iterator method affects remove the behavior of the method. It also describes iterator how the iterator behavior returned by the method affects the remove behavior of the method. Contrary to the case in entry 18, in this case, the programmer HashSet 's inheritance does not indicate whether overriding the Add method will affect the behavior of the AddAll method.

But does this violate a good API document that should describe what a given method is, rather than how it is done? Yes, it does! This is an unfortunate consequence of the fact that inheritance violates encapsulation. To document a class so that it can be safely subclass, you must describe the implementation details that are not described in detail.

@implSpecTags are added in Java 8 and are used extensively in Java 9. This tag should be enabled by default, but starting with Java 9, the -tag "apiNote:a:API Note:” Javadoc utility will ignore it unless it is through a command-line switch.

Design inheritance involves more than just a pattern of document descriptions for personal use. In order for programmers to write effective subclasses without undue pain, a class might provide internal work in the form of a protected method of choice, or, in rare cases, provide protected properties. For example, consider the removerange method in java.util.AbstractList :

protected void RemoveRange (int fromIndex, int toindex) removes from this list all of the elements whose index is between FR Omindex, inclusive, and Toindex, exclusive. Shifts any succeeding elements to the left (reduces their index). This is the shortens the list by (Toindex-fromindex) elements. (If Toindex = = FromIndex, this operation have no effect.) This method was called by the clear operation on this list and its sublists. Overriding this method to take advantage of the internals of the list implementation can substantially improve the perform Ance of the clear operation on this list and its sublists. Implementation Requirements:this implementation gets a list iterator positioned before FromIndex and repeatedly calls Lis Titerator.nextfollowed by Listiterator.remove, until the entire range has been removed. Note:if Listiterator.remove requires linear time, this implementation requires quadratic time. Parameters:fromindex index of first element to be Removed.toindex indexAfter the last element to be removed. 

Removes fromIndex all elements between (contained) and inclusive (not included) in the index from this list. Moves any subsequent elements to the left (reducing the index). This call uses (toIndex - fromIndex) elements to shorten the list. (if toIndex == fromIndex , this operation is not valid.) )

This method is called by the clear operation of the list and its subclasses. Overriding this method takes advantage of the internal implementation of the list, which can greatly improve the clear operation performance of lists and subclasses.

Implementation requirements: This implementation gets a list iterator, which is located fromIndex before, and repeats the call ListIterator.remove and ListIterator.next method until the entire scope is deleted. Note: ListIterator.remove This implementation requires a square-level time if linear time is required.

Parameters:
FromIndex the index of the first element to remove
Toindex the index after the last element to remove

This method is meaningless to the end user of the list implementation. It is just to make the subclass easy to provide a quick clear method. In the absence removeRange of a method, when the clear method is called on the sub-list, the subclass will have to use the square-level time, otherwise, or rewrite the entire sublist mechanism from scratch-it's not an easy thing!

So when you design an inheritance class, how do you decide which protected members to expose? Unfortunately, there is no panacea. The best thing you can do is to think hard, make the best tests, and then test them by writing subclasses. Protected members should be exposed as little as possible because each member represents a commitment to the implementation details. On the other hand, you can't expose too little, because losing a protected member can cause a class to be almost impossible to inherit.

The only way to test a class that is designed for inheritance is to write subclasses . If you neglect a key protected member, trying to write a subclass will make the omission painfully apparent. Conversely, if you write several subclasses and none of them use a protected member, you should make them private. Experience has shown that three subclasses are usually sufficient to test an inheritable class. These subclasses should be written by someone other than the author of the parent class.

When you design a class that is likely to be widely used for inheritance, realize that you are always committed to your own patterns of document descriptions and to the implementation of the methods and properties that are implied in their protection. These commitments may make it difficult or impossible to improve the performance or functionality of the class in subsequent releases. So before you publish it, you have to test your class by writing a subclass .

Also, note that the special documentation required for inheritance is confusing the normal document, which is designed for programmers who create instances of classes and invoke methods on them. At the time of writing, there were few tools to separate the normal API documentation from and just the information implemented by the subclass.

There are also classes that must adhere to the restrictions that allow inheritance. The constructor method must not invoke the Overridable method directly or indirectly . Violating this rule will cause the program to fail. The parent class construction method runs before the subclass constructs the method, so the overridden method in the subclass is called before the subclass construction method runs. If the overriding method relies on any initialization performed by the subclass construction method, this method will not run as expected. To specify this, this is a class that violates this rule:

public class Super {    // Broken - constructor invokes an overridable method    public Super() {        overrideMe();    }    public void overrideMe() {    }}

The following is a subclass of an overriding method that is overrideMe called incorrectly by the Super class's unique construction method:

public final class Sub extends Super {    // Blank final, set by constructor    private final Instant instant;    Sub() {        instant = Instant.now();    }    // Overriding method invoked by superclass constructor    @Override public void overrideMe() {        System.out.println(instant);    }    public static void main(String[] args) {        Sub sub = new Sub();        sub.overrideMe();    }}

You may expect this program to print two instant instances, but it prints null for the first time because it instant overrideMe is called by the Super constructor method before the sub constructor method has a chance to initialize the property. Please note that this program observes the final properties of two different states! Also note that if the overrideMe method invokes instant any of the methods in the instance, overrideMe it throws an exception when the parent class constructs the method call NullPointerException . The only reason this program will not throw NullPointerException is because the println method tolerates null parameters.

Note that calling private methods from a constructor method, where none of the methods can be overridden, is safe for the final and static methods.

Cloneableand Serializable interfaces create special difficulties when designing inheritance. Implementing these interfaces is often not a good idea for classes designed for inheritance, because it can be a huge burden on the programmer who inherits the class. However, special actions can be taken to allow subclasses to implement these interfaces without forcing them to do so. These operations are described in entry 13 and entry 86.

If you decide to implement or interface in a class designed for inheritance, you Cloneable Serializable should be aware that because the clone and readObjec T methods are similar to the constructor method, there are similar limitations:both clone and ReadObject do not invoke overridable methods either directly or indirectly . In readObject the case, the overriding method runs before the state of the child class is deserialized. In clone the case, the overriding method will run before the method of the subclass clone has the opportunity to repair the cloned state. In either case, a program failure can occur. In clone the case of a failure, the original object and the cloned object itself can be damaged. This can happen, for example, if the overriding method assumes that it is modifying a copy of the object's deep structure, but has not yet created a copy.

Finally, if you decide to implement an interface in a class designed for inheritance Serializable , and the class has one readResolve or writeReplace method, you must make the readResolve or writeReplace method set to protected instead of private. If these methods are private, they are silently ignored by the quilt class. This is another case where implementing details become part of the class's API to allow inheritance.

So far, it takes a lot of effort to design an inheritance class, and there are a lot of limitations to this class . This is not a rash decision. Some cases are clearly correct, such as abstract classes, including skeleton implementations of interfaces (skeletal implementations) (entry 20). There are other things that are obviously wrong, such as immutable classes (entry 17).

But what about ordinary, concrete classes? Traditionally, they are neither final nor designed and documented for the sake of subclasses, but this situation is dangerous. Each time you modify such a class, the subclass that inherits this class is destroyed. This is not just a theoretical question. After modifying the interior of a non-final concrete class, it is not uncommon to receive error reports related to subclasses that are not designed and documented for inheritance.

The best way to solve this problem is to prohibit subclasses in classes that do not want to safely subclass the design and documentation descriptions . There are two ways of prohibiting subclasses. It is easier to declare the class final. Another approach is to make all construction methods private or package-level private, and to add a public static factory instead of a construction method. This scenario provides the flexibility to use subclasses internally, as discussed in article 17. Both of these methods are acceptable.

This suggestion may be controversial because many programmers are accustomed to inheriting common, concrete classes to add functionality, such as notifications and synchronization, or to restrict the functionality of the original class. If a class implements some of the interfaces that capture its essence, such as set,list or map, you should not feel guilty about banning subclasses. The wrapper class pattern described in entry 18 provides a superior choice of inheritance for enhanced functionality.

If a specific class does not implement a standard interface, you may inconvenience some programmers by banning inheritance. If you think you must allow inheritance from such a class, a reasonable way is to make sure that the class never calls any overridable methods, and that the document describes the fact. In other words, completely eliminate the overridable method of the class for self-use (self-use). By doing this, you will create a reasonably safe subclass. Overriding a method does not affect the behavior of any other methods.

You can mechanically eliminate the overriding method of the self-use of the class without altering its behavior. Move the body of each overridable method to a private helper method, and have each overridable method call its private helper method. Then substitute a dedicated helper method that calls the overridable method directly to replace each self-used overridable method.

You can mechanically eliminate the override method of the class for your own use without altering its behavior. Move the body of each overridable method to a private helper method, and have each overridable method call its private helper method. Then substitute a dedicated helper method that calls the overridable method directly to replace each self-used overridable method.

In short, designing an inheritance class is a very hard thing to do. You must document all your own patterns, and once you document them, you must commit to their entire life cycle. If you do not, subclasses may depend on the implementation details of the parent class, and if the implementation of the parent class changes, the subclass may become corrupted. To allow others to write efficient subclasses, you may also need to export one or more protected methods. Unless you know that there is a real subclass to take, you might be better off by declaring your class as final, or by making sure that there is no accessible constructor method.

Effective Java Third edition--19. If you use inheritance then design and document the description, otherwise you should not use

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.