Valid Java, inclutivejava
It is very common to add restrictions to parameters of a method. For example, a parameter indicates that the index cannot be a negative number or a key object reference cannot be null. Otherwise, some processing will be performed, for example, an exception message is thrown.
For these parameter restrictions, the method provider must specify in the document, check the parameters at the beginning of the method, and provide clear information when the failure occurs, that is:
detect errors as soon as possible after they occur
This will be a major guarantee for accurate error locating.
If this is not done, the best case is that the method fails during the processing and an inexplicable exception is thrown. The source of the error becomes difficult to locate, but this is the best case.
The worse case is that the method is passed and no error occurs, but the result is totally different from the method description. Finally, when a key part sees strange data, it cannot be recovered.
We usually throw an exception class for parameter violation.IllegalArgumentException
,IndexOutOfBoundsException
,NullPointerException
.
The Javadoc @ throws label must be used to describe the exception type thrown when the constraint is violated.
In addition, not all parameter checks require this.
If the method or constructor is not exported, you can simply useassert
To ensure the validity of the parameter.
For example, java. util. Collections $ CopiesList:
private static class CopiesList<E> extends AbstractList<E> implements RandomAccess, Serializable{ //... CopiesList(int n, E e) { assert n >= 0; this.n = n; element = e; } //...}
However, the validity check is not simple, and the cost of this operation may be very large or even impractical. Therefore, some operations directly hide the parameter check in the calculation process.
For examplejava.util.Collections
Ofsort()
Methods. All the elements in the list can be compared. The method does not check validity at the beginning, but throws an exception when a problem occurs in calculation.
Compared with this method, it is of little significance to check validity in advance.
However, to check the validity of parameters during calculation, consider the atomicity of the method.
When talking about the parameters, you have to say that the method is overloaded. First, the example is as follows:
import java.math.BigInteger;import java.util.ArrayList;import java.util.Collection;import java.util.HashMap;import java.util.HashSet;import java.util.List;import java.util.Set;public class CollectionClassifier { public static String classify(Set<?> s) { return "Set"; } public static String classify(List<?> lst) { return "List"; } public static String classify(Collection<?> c) { return "Unknown Collection"; } public static void main(String[] args) { Collection<?>[] collections = { new HashSet<String>(), new ArrayList<BigInteger>(), new HashMap<String, String>().values() }; for (Collection<?> c : collections) System.out.println(classify(c)); }}
A very common pen exam will output three "Unknown Collection". The method to be called is determined during compilation.
And static selection of the overload method (why did we design the overload? Overriding is the norm and overloading is the exception, it seems that no one wants this method) is relative to the override method, and the choice of overridden method is dynamic.
That is, the selection method is executed at runtime, And the subclass uses the same method signature to overwrite the upper-level class method. If this method is an instance method, it will be called on the subclass instance.
For example, in the following example, the instance compilation type does not affect it:
class Wine { String name() { return "wine"; }}class SparklingWine extends Wine { @Override String name() { return "sparkling wine"; }}class Champagne extends SparklingWine { @Override String name() { return "champagne"; }}public class Overriding { public static void main(String[] args) { Wine[] wines = { new Wine(), new SparklingWine(), new Champagne() }; for (Wine wine : wines) System.out.println(wine.name()); }}
In view of this feature of the overload method, and the language itself has no special restrictions on it, the author suggests <do not provide a reload method with the same number of parameters>, and do not consider overloading for variable parameters.
A good example of this suggestion is ObjectOutputStream. For its write method, the designer does not provide reloads with the same number of parameters, but provides methods such as writeInt, writeBoolean, and writeLong, the read method is also symmetric with the write method.
This method is not applicable to constructors. We cannot name constructors, but we can privatize constructors and export static factories.
But it cannot be said that such rules must be strictly followed, for examplefetchSalaryInfo()
Two methods are provided for overloading: int uid and User userInfo. It is hard to imagine that a call error may occur.
Damage <do not provide overload methods with the same number of parameters> This rule does not only appear when exporting a method, but also when updating an existing class.
For example, the String class has a since 1.4 contentEquals (StringBuffer), and a since 1.5 contentEquals (CharSequence ).
(The CharSequence interface is added in Java 1.5 and serves as a public interface for StringBuffer, StringBuilder, CharBuffer, String, and other classes.) But this does not cause harm, because the behavior of the two overload methods in this example is exactly the same.
The following interesting situations are available for generics:
import java.util.ArrayList;import java.util.List;import java.util.Set;import java.util.TreeSet;public class SetList { public static void main(String[] args) { Set<Integer> set = new TreeSet<Integer>(); List<Integer> list = new ArrayList<Integer>(); for (int i = -3; i < 3; i++) { set.add(i); list.add(i); } for (int i = 0; i < 3; i++) { set.remove(i); list.remove(i); } System.out.println(set + " " + list); }}
Slot: are the parameters in remove index or element?
The execution result is [-3,-2,-1] [-2, 0, 2], that is, the parameter in list. remove is index, rather than automatically boxed.
It should be said that the root cause of the problem is that List provides bothremove(int)
Andremove(E)
?
But as API users, what we can do is to be more cautious with this feature of Java 5.