Item 38 Check parameters for validity
You shoud attempt to detect errors as soon as possible after they occur. Otherwise, it may have some bad impact, such as wrong answers and memory leak.
For public methods, use @throws tag to document the exception that will be thrown if a restriction on parameter values is violated.
/**
* Returns a BigInteger whose value is (this mod m). This method differs
* from the remainder method in that it always returns a non-negtive
* BigInteger.
*
* @param m
* the modulusm which must be positive
* @return this mod m
* @throws ArithmeticException
* if m is less than of equal to 0
*/
public BigInteger mod(BigInteger m) {
if (m.signum() <= 0)
throw new ArithmeticException("Modulus <= 0: " + m);
// Do the computation
}
// Private helper function for a recursive sort
private static void sort(long a[], int offset, int length) {
assert a != null;
assert offset >= 0 && offset <= a.length;
assert length >= 0 && length <= a.length - offset;
// Do the computation
}
For an unexported method, check their parameters withassertions that claim condition should be true. If they fail, assertions will throw AssertionError and have no effect and no cost if you don't enable assertions.
It's important to check the validity of parameters that are not used by a method but are stored away for later use, e.g Constructors.
Dont check validity if the check isexpensive or impractical and the computation implicitly check the validity. e.g Collections.sort(list), when compare elements can throw ClassCastException.
We should design methods to be as general as it is pratical to make them. Fewer restrictions means better flexibility.
In summary, when we write method or constructor, we should think about what restrictions exist on its parameters.We should document these restrictions and enforce them with explicit checks. This work will be paid back with interest the first time a validity check fails.
Item 39 Make defensive copies when needed
Java is a safe language, in the absence of native methods, it is immune to buffer overruns, array overruns, wild pointers and other memory corruption errors that plague unsafe language such as C and C++. We can control the state of class.
We must program defensively, with the assumption that clients of our class will do their best to destroy its invariants.
In order to protect classes from unintented destroy, it's essential to make a defensive copy of each mutable parameter to the constructor.
Defensive copy are made before checking the validity of the parameters and the validity check is performed on the copies rather than on the originals. So it can avoid TOCTOU (time of check/time of use) attack.
Do not use the clone method to make a defensive copy of a parameter whose type is subclassable by untrusted parties. e.g Date is nonfinal, the clone method is not guaranteed to return an object whose class is java.util.Data; it could return an instance of an untrusted subclass.
//Broken 'immutable' time period class
public final class Period {
private final Date start;
private final Date end;
/**
* @param start the beginning of the period
* @param end the end of the period; must not precede start
* @throws IllegalArgumentException if start is after end
* @throws NullPointerException if start or end is null
*/
public Period(Date start, Date end){
if(start.compareTo(end) > 0)
throw new IllegalArgumentException(start + " after " + end);
this.start = start;
this.end = end;
}
public Date start() {
return start;
}
public Date end() {
return end;
}
}
//Repaired constructor - make defensive copies of parameters
public Period(Date start, Date end){
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if(this.start.compareTo(this.end) > 0)
throw new IllegalArgumentException(start + " after " + end);
}
//Repaired accessors - make defensive copies of internal fields
public Date start() {
return new Date(start.getTime());
}
public Date end() {
return new Date(end.getTime());
}
Accessors should return defensive copies of mutable internal fields. You can use clone now, because we know the internal class.
When we write a method or constructor that enters a cilent-provided object into a internal data structure. Think about:
1. whether client-provided object is mutable.
2. whether our class can tolerate a change in that object.
if so, we should do defensive copies.
If we return a reference to an internal component that is mutable, we need return a defensive copy or an immutable view of the array. Remember that an internal array is mutable.
But we'd better use immutable objects as components of our objects, so we don't have to do defensive copies. e.g use Date.getTime() to represent internal time rather than using a Date reference because Date is mutable and long is not.
Classes containing methods or constructors whose invocation indicates a transfer of control cannot defend themselves against malicious clients.
In summary, if a class has mutable components that it gets from or returns to its clients, the class must defensively copy these components. If the cost is huge and class trusts clients, we could add documents outlining the clients' responsibility not to modify the affected components.
Item 40 Design method signatures carefully
API design hints:
- Choose method names carefully:
1. understandable and consistent with other names in the same package.
2. consistent with the broader consensus.
- Dont go overboard in providing convenience methods:
1. keep class/interface light and small
2. consider providing a shorthand only if it will be used often. When in doubt, leave it out.
- Avoid long parameter lists:
1. less than 4
2. long sequences of identically typed parameters are especially harmful. -> can't remember
2.1 break method up into multiple methods, e.g List provide subList and indexOf to yield certain functionality.
2.2 create helper class (static member class) to hold groups of parameters, if a frequently occurring sequence of parameters is seen to represent some distinct entity.
2.3 Builder pattern.
- For parameter types, favor interfaces over classes:
1. if use concrete class, clients need do some expensive copy operation if input data has other forms.
- Prefer two-element enum types to boolean parameters:
1. it's easy to read and write and add more options.
2. can also get more info about the enum type.
Item 41 Use Overloading judiciously(明智而审慎的)
the choice of which overloading to invoke is made at compile time.
selection among overloaded methods is static (at compile time), while selection among overriden methods is dynamic (at run time).
replace all overloadings with a single method that does an explicit instanceof test to find the right type.
Avoid confusing uses of overloading.
A safe, conservative policy is never to export two overloadings with the same number of parameters.
We can also use different names. e.g in ObjectOutputStream, it provides writeInt, writeLong rather than overloading.
For constructors, they only can be overloaded.
After 1.5, we have autoboxing and generics, it caused some trouble.
In summary, you should refrain from overloading methods with multiple signatures that have the same number of parameters. Avoid situations where the same set of parameters can be passed to different overloadings by the addition of casts. If can't avoid, try to ensure that all overloadings behave identically when passed the same parameters.
Item 42 Use varargs judiciously
Variable arity methods: accept >= 0 arguments of a specific type.
Firstcreate an array whose size is the number of arguments passed at the call site, thenputting the values into the array and pass the array to the method. It is designed for printf (since 1.5) and for the core reflection facility.
Check args length explicitly and may encounter error during run-time. It's ugly!
Arrays.asList don't use varargs because when printing array, the idiom will only work on arrays of object references, if we put primitive types into it. It will cause compile error. After 1.5, it will compile and run, and we can print the List<int[]> instance's address, which is useless. We can useArrays.toString to print an array.
//The right way to use varargs to pass one or more arguments
static int min(int firstArg, int... remaningArgs){
int min = firstArg;
for(int arg: remaningArgs){
if(arg < min)
min = arg;
}
return min;
}
//The right way to print an array
System.out.println(Arrays.toString(myArray));
Don't retrofit(改进) every method that has a final array parameter; use varargs only when a call really operates on a variable-length sequence of values.
Every invocation of a varargs method causes an array allocation and initialization. It's not quite efficient.
If we can decide most calls have certain number N of args, we can create N overloading methods and another method with varargs.
e.g EnumSet static factory use this technique.
In summary, varargs can provide variablility for args but they shouldn't be overused, they can produce confusing results if used unappropriately.
Item 43 Return empty arrays or collections, not nulls
Some might think return nulls avoid the expense of allocating the array.
1. it's inadvisable to worry about performance unless this method is really contributor to performance problem.
2. it's possible to return the same zero length array from every invocation that returns no items because 0-length arrays are immutable that may be shared freely.
//The right way to return an array from a collection
private final List<Cheese> cheeseInStock = ...;
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];
/**
* @return an array containing all of the cheese in the shop
*/
public Cheese[] getCheese()
{
return cheeseInStock.toArray(EMPTY_CHEESE_ARRAY);
}
//The right way to return a copy of a collection
public List<Cheese> getCheeseList(){
if(cheeseInStock.isEmpty()){
return Collections.emptyList(); //Always returns same list
}
else {
return new ArrayList<Cheese>(cheeseInStock);
}
}
In summary, there is no reason ever to return null from an array- or collection-valued method instead of returning an empty array or collection.
The null-return idiom is likely a holdover from the C programming, in which array lengths are returned separately from actual arrays. In C, there is no advantage to allocating an array if zero is returned as the length.
Item
44 Write doc comments for all exposed API elements
Javadoc generates API documentation automatically from source code with specially formatted documentation comments, more commonly known as doc comments.
Read Sun's How to Write Doc Comments.
From 1.5, {@literal} and {@code} are added.
//Doc comments convention
/**
* Returns the element at the specified position in this list.
*
* <p>
* This method is <i>not</i> guaranteed to run in constant time. In some
* implementations it may run in time proportional to the element position.
*
* @param index
* index of element to return; must be non-negative and less than
* the size of this list
* @return the element at the specified index position in this list
* @throws IndexOutOfBoundsException
* if the index is out of range (
* {@code index < 0 || index >= this.size()} )
*
*/
E get(int index);
To document your API properly, you must precede every exported class, interface, constructor, method and field declaration with a doc comment.
If a class is serializable, you should document its serialized form.
To write maintainable code, you should also write doc comments for most unexported classes and so on.
The doc comment for a method should describe succinctly the contract between the method and its client.
Except abstract methods which just need say what they do rather than how.
1. code comment should enumerate all of the method's precondition, such as @throws + if (the condition under which the exception is thrown) for unchecked exceptions and @param/return + noun for the affected parameters.
2. document side effects that change the state of the system
3. describe the thread safety of a class or method
HTML tags are used to be translated to HTML document.
{@code}:
1. causes the code fragment to be rendered in code font.
2. suppress processing of HTML makeup and nested Javadoc tags in code fragment. E.g allow use of <>
It is no longer necessary to use the HTML <code> , <tt> tags in doc comments: the Javadoc {@code} is preferable because it eliminates the need to escape HTML metacharacters.
Precede multiline code with <pre> {@code and follow it with the characters} </pre>
By convention, 'this' always refers to the object on which the method is invoked when it is used in the doc comment for an instance method.
Use {@literal} to add special characters. It is like the {@code} tag, but doesn't render the text in code font.
Generated doc readability trumps (胜过) source code readability.
The first sentence of a comment should summarize the element.
To avoid confusion, no two members or constructors in a class or interface should have the same summary description.
For methods and constructors, the summary should be a full verb phrase describing the action.
For classes, interfaces, and fields, the summary description should be a noun phrase describing the thing reprented by it.
After 1.5, generics, enums and annotations are added.
When documenting a generic type or method, be sure to document all type parameter.
/**
* An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value.
*
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
*/
public interface Map<K, V>{
}
When documenting an enum type, be sure to document the constant, type and any public methods.
/**
* An instrument section of a symphony orchestra.
*/
public enum OrchestraSection{
/** Woodwinds, such as flute, clarinet and oboe.*/
WOODWIND,
/** Brass instruments, such as french horn and trumpet.*/
BRASS,
/** Percussion instruments, such as violin and cello.*/
PERCUSSION;
}
When documenting an annotation type, be sure to document any members and itself.
/**
* Indicates that the annotated method is a test method that must throw the
* designated exception to succeed.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
/**
* The exception that the annotated test method must throw in order to
* pass. (The test is permitted to throw any subtype of the type
* described by this class object.)
*
* @return
*/
Class<? extends Throwable> value();
}
Javadoc can inherit method comments. Classes can reuse doc comments from superclasses rather than copy. But it's also tricky.
To summarize, doc comments are the best, most effective way to document your API. Their use can be considered mandatory for all exported API elements. Adopt a consistent style that adheres to standard conventions. Remember that arbitrary HTML is permissible within documentation comments and that HTML metacharacters must be escaped.