ITEM 10: Obey the general contract when overriding equals
In summary, don’t override the equals method unless you have to: in many cases, the implementation inherited from Object does exactly what you want. If you do override equals , make sure to compare all of the class’s significant fields and to compare them in a manner that preserves all five provisions of the equals contract.
ALWAYS OVERRIDE hashcode WHEN YOU OVERRIDE equals
- One-line hashCode method - mediocre performance
@Override
public int hashCode() {
return Objects.hash(lineNum, prefix, areaCode);
}
If a class is immutable and the cost of computing the hash code is significant, you might consider caching the hash code in the object rather than recalculating it each time it is requested. Otherwise, you might choose to lazily initialize the hash code the first time hash-Code is invoked.
- hashCode method with lazily initialized cached hash code
private int hashCode; // Automatically initialized to 0
@Override
public int hashCode() {
int result = hashCode;
if (result == 0) {
result = Short.hashCode(areaCode);
result = 31 * result + Short.hashCode(prefix);
result = 31 * result + Short.hashCode(lineNum);
hashCode = result;
}
return result;
}
- Do not be tempted to exclude significant fields from the hash code computation to improve performance.
- Don’t provide a detailed specification for the value returned by hashCode , so clients can’t reasonably depend on it; this gives you the flexibility to change it.
ITEM 12: Always override toString
ITEM 13: Override clone judiciously
- in practice, a class implementing
Cloneable
is expected to provide a properly functioning public clone method.
x.clone() != x // true
x.clone().getClass() == x.getClass() // true
x.clone().equals(x) // true
- immutable classes should never provide a
clone
method - In effect, the clone method
functions as a constructor; you must ensure that it does no harm to the original object and that it properly establishes invariants on the clone - the Cloneable architecture is incompatible with normal use of final fields referring to mutable objects
- A better approach to object copying is to provide a copy constructor or copy factory.
Implementing comparale
Unlike the other methods discussed in this chapter, the compareTo
method is not declared in Object
. Rather, it is the sole method in comparable Comparable
inteface. It is similer in charater to Object's equals
method, except that is permits order comparisons in addition to simple equality comparisons, and it is generic. By implementing Comparable
, a class indicates that its instance have a natural ordering. Sorting an array of objects that implement Comparable
is as simple as this:
Arrays.sort(a);
It is similarly easy to serch, compute extreme values, and maintain automatically sorted collections of Comparable
Objects. For example, the following program, which relies on the face that String
implements Comparable
, prints an alphabetized list of its command-line arguments whit duplicates eliminated:
public class WordList{
public static void main(String[] args) {
Set<String> s = new TreeSet<>();
Collections.addAll(s, args);
System.out.println(s);
}
}
By implements Comparable
, you allow your class to interoperate whit all of the many generic algorithms and collection implementations that depend on this interface. You gain a tremendous amount of power for a small amount of effort. Virtually all of the value classes in the Java platform libraries, as well as all enum types, implement Comparable
. If you are writing a value class with an obvious natural ordering, such as alphabetical order, numerical order, or chronological order, you should implement the Comparable
interface:
public interface Comparable<T> {
int comparaTo(T t);
}
The general contract of the comparaTo
method is similar to that of equals
:
Comparaes this object whit the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
Throws classCastException
if the specified object's type prevents it from being compared to this object.
In the following descripetion, the nataion sgn
(expression) designates the mathematical siginum function, which is defined to -1, 0, 1, according to whether the value of expression is negative, zero, or positive/
- The implementor must ensure that
sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
for allx
andy
.(This implies thatx.compareTo(y)
must thorw an exception if and only ify.compareTo(x)
throws an exception.) - The implementtor must also ensure that the relation is transitive:
(x.compareTo(y) > 0 && y.compareTo(z)) > 0
impliesx.compareTo(z) > 0
. - Finally, the iplementor must ensure that
x.compareTo(y) == 0
implies thatsgn(x.compareTo(z)) == syn(y.compareTo(z))
, for allz
. - It is Strongly recommended, but not requured, that implements the
Comparable
interface and violates this condition should clearly indicate this fact. The recommended language is "Node: This class has a natural ordering that is inconsistent withequals
."
Writing a compareTo
method is similar to writing an equals
method, but there are a few key differents, Because the Comparable
interface is parameterized, the compareTo
method is statically typed, so you don't need to type check or cast its argument.
Use of the relational operators <and> in compareTo
methods is verbose and error-prone and no longer recommended.
- Multiply-field Comparable with primitive fields
public int compareTo(PhoneNumber pn) {
int result = Short.compare(areaCode, pn.areaCode);
if (result == 0) {
result = Short.compare(prefix, pn.prefix);
if (reuslt == 0) {
result = Short.compare(lineNum, pn.lineNum);
}
}
}
- Comparable with comparator construction methods
private static final Comparator<PhoneNumber> COMPARATOR =
comparingInt((PhoneNumber pn) -> pn.areaCode)
.thenComparingInt(pn -> pn.prefix)
.thenComparingInt(pn -> pn.lineNum);
public int compareTo(PhoneNumber pn) {
return COMPARATOR.compare(this, pn);
}
Occasionally you may seecompareTo
or compare
methods that rely on the fact that the difference between two values is negative if the first value is less than second, zero if the two values are equals, and postive if the first value is greater. Here is an example:
- BROKEN difference-based comparator-violates transitivity!
static Comparator<Object> hashCodeOrder = new Comparator<>(){
public int compare(Object o1, Object o2) {
return o1.hashCode() - o2.hashCode();
}
};
- Comparator based on static compare method
static Comparator<Object> hashCodeOrder = new Comparator<>(){
public int compare(Object o1, Object o2) {
return Integer.compare(o1.hashCode(), o2.hashCode());
}
};
- Comparator based on Comparator construction method
static Comparator<Object> hashCodeOrder =
Comparator.comparingInt(o -> o.hashCode());
In summary, whenever you implements a value class that has a sensible ordering, you should hava the class implement the Comparable
interface so that its instances can be easily sored, serached, and used in comparison-based collections. When comparing field values in the implementations of the compareTo
methods, avoid the use of the <and> operators. Instead, use the static compare
methods in the boxed primitive classes or the comparator constructtion methods in the Comparator
interface.
网友评论