覆盖equals【引自http://www.javapractices.com/Topic17.cjp】

来源:互联网 发布:安卓 intent获取数据 编辑:程序博客网 时间:2024/06/05 10:04
Implementing equalsDiscussion:
All objects have both identity (the object's location in memory) and state (the object's data). The == operator always compares identity. The default implementation of equals compares identity as well.

Sometimes the default implementation of equals has the desired behaviour (as in a type-safe enumeration, for example), but  equals should usually compare state, not identity. This is particularly true for "data-centric" classes which map to database records.

hashCode and equals are closely related :

  • if you override equals, you must override hashCode
  • hashCode must generate equal values for equal objects
Objects placed in a List , Set, or Map (as either a key or value) should have an appropriate definition of equals. (See, for example, the javadoc for Collection.contains , Map.containsKey, and Map.containsValue .)

If you extend a concrete class, and add a new field which contributes to equals, then it is not possible to write a perfectly correct equals method for the new class. Instead, you should use composition instead of inheritance.

Example

Here is an implementation of equals for a data-centric class. It demonstrates how different types of fields are treated:

  • object fields, including collections : use equals
  • type-safe enumerations : use either equals or == (they amount to the same thing, in this case)
  • possibly-null object fields : use both == and equals
  • array fields : use Arrays.equals
  • primitive fields other than float or double : use ==
  • float : convert to int using Float.floatToIntBits, then use ==
  • double :  convert to long using Double.doubleToLongBits, then use ==
It is worth noting that if fields are implemented with wrapper classes (Integer, Boolean, and so on), then implementation of equals is simpler, since there is only one case : calling the equals method recursively. (The compareTo method is also simplified in this case.)

The above policies can be collected in a utility class :

import java.util.Arrays;/*** Collected methods which allow easy implementation of <code>equals</code>.** Example use case in a class called Car:* <pre>public boolean equals(Object that){  if ( this == that ) return true;  if ( !(that instanceof Car) ) return false;  Car thatCar = (Car)that;  return    EqualsUtil.areEqual(this.fName, that.fName) &&    EqualsUtil.areEqual(this.fNumDoors, that.fNumDoors) &&    EqualsUtil.areEqual(this.fGasMileage, that.fGasMileage) &&    EqualsUtil.areEqual(this.fColor, that.fColor) &&    Arrays.equals(this.fMaintenanceChecks, that.fMaintenanceChecks); //array!}* </pre>** <em>Arrays are not handled by this class</em>.* This is because the <code>Arrays.equals</code> methods should be used for* array fields.*/public final class EqualsUtil {  static public boolean areEqual(boolean aThis, boolean aThat){    //System.out.println("boolean");    return aThis == aThat;  }  static public boolean areEqual(char aThis, char aThat){    //System.out.println("char");    return aThis == aThat;  }  static public boolean areEqual(long aThis, long aThat){    /*    * Implementation Note    * Note that byte, short, and int are handled by this method, through    * implicit conversion.    */    //System.out.println("long");    return aThis == aThat;  }  static public boolean areEqual(float aThis, float aThat){    //System.out.println("float");    return Float.floatToIntBits(aThis) == Float.floatToIntBits(aThat);  }  static public boolean areEqual(double aThis, double aThat){    //System.out.println("double");    return Double.doubleToLongBits(aThis) == Double.doubleToLongBits(aThat);  }  /**  * Possibly-null object field.  *  * Includes type-safe enumerations and collections, but does not include  * arrays. See class comment.  */  static public boolean areEqual(Object aThis, Object aThat){    //System.out.println("Object");    return aThis == null ? aThat == null : aThis.equals(aThat);  }} 


Car is a class which uses EqualsUtil to implement its equals method :
import java.util.*;public final class Car {  public Car (    String aName,    int aNumDoors,    List<String> aOptions,    double aGasMileage,    String aColor,    Date[] aMaintenanceChecks  ){    fName = aName;    fNumDoors = aNumDoors;    fOptions = new ArrayList<String>(aOptions);    fGasMileage = aGasMileage;    fColor = aColor;    fMaintenanceChecks = new Date[aMaintenanceChecks.length];    for (int idx=0; idx < aMaintenanceChecks.length; ++idx) {      fMaintenanceChecks[idx] = new Date( aMaintenanceChecks[idx].getTime() );    }  }  public boolean equals(Object aThat) {    //check for self-comparison    if ( this == aThat ) return true;    //use instanceof instead of getClass here for two reasons    //1. if need be, it can match any supertype, and not just one class;    //2. it renders an explict check for "that == null" redundant, since    //it does the check for null already - "null instanceof [type]" always    //returns false. (See Effective Java by Joshua Bloch.)    if ( !(aThat instanceof Car) ) return false;    //Alternative to the above line :    //if ( aThat == null || aThat.getClass() != this.getClass() ) return false;    //cast to native object is now safe    Car that = (Car)aThat;    //now a proper field-by-field evaluation can be made    return      EqualsUtil.areEqual(this.fName, that.fName) &&      EqualsUtil.areEqual(this.fNumDoors, that.fNumDoors) &&      EqualsUtil.areEqual(this.fOptions, that.fOptions) &&      EqualsUtil.areEqual(this.fGasMileage, that.fGasMileage) &&      EqualsUtil.areEqual(this.fColor, that.fColor) &&      Arrays.equals(this.fMaintenanceChecks, that.fMaintenanceChecks);  }  //..other methods elided  // PRIVATE ////  /**  * The following fields are chosen to exercise most of the different  * cases.  */  private String fName;  private int fNumDoors;  private List<String> fOptions;  private double fGasMileage;  private String fColor; //treat as possibly-null  private Date[] fMaintenanceChecks;  /**  * Exercise the equals method.  */  public static void main (String[] aArguments) {    List<String> options = new ArrayList<String>();    options.add("sunroof");    Date[] dates = new Date[1];    dates[0] = new Date();    //Create a bunch of Cars; only one and two should be equal    Car one = new Car("Nissan", 2, options, 46.3, "Green", dates);    //two is equal to one    Car two = new Car("Nissan", 2, options, 46.3, "Green", dates);    //three has a differs in fName only    Car three = new Car("Pontiac", 2, options, 46.3, "Green", dates);    //four differs in fNumDoors only    Car four = new Car("Nissan", 4, options, 46.3, "Green", dates);    //five differs in fOptions only    List<String> optionsTwo = new ArrayList<String>();    optionsTwo.add("air conditioning");    Car five = new Car("Nissan", 2, optionsTwo, 46.3, "Green", dates);    //six differs in fGasMileage only    Car six = new Car("Nissan", 2, options, 22.1, "Green", dates);    //seven differs in fColor only    Car seven = new Car("Nissan", 2, options, 46.3, "Fuchsia", dates);    //eight differs in fMaintenanceChecks only    Date[] datesTwo = new Date[1];    datesTwo[0] = new Date(1000000);    Car eight = new Car("Nissan", 2, options, 46.3, "Green", datesTwo);    System.out.println( "one = one: " + one.equals(one) );    System.out.println( "one = two: " + one.equals(two) );    System.out.println( "two = one: " + two.equals(one) );    System.out.println( "one = three: " + one.equals(three) );    System.out.println( "one = four: " + one.equals(four) );    System.out.println( "one = five: " + one.equals(five) );    System.out.println( "one = six: " + one.equals(six) );    System.out.println( "one = seven: " + one.equals(seven) );    System.out.println( "one = eight: " + one.equals(eight) );    System.out.println( "one = null: " + one.equals(null) );  }} 


An example run of this class demonstrates that only objects one and two are equal :
one = one: true
one = two: true
two = one: true
one = three: false
one = four: false
one = five: false
one = six: false
one = seven: false
one = eight: false
one = null: false
原创粉丝点击