Java开发之道
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

陷阱21 一字之差——重写对象的hashCode方法,但重写equals方法时参数不是Object类型

在进行程序开发时重写了equals方法,但是没有重写hashCode方法,会导致程序在某些情况下出错,同样,如果重写了对象的hashCode方法,但是没有重写equals方法或重写equals方法时参数不是Object类型,程序在某些情况下也一样会出现错误。

注意

Object类的equals方法其参数必须是Object类型的,如果其直接子类或间接子类重写该方法,一定要保证其参数也是Object类型,否则在进行一些特殊处理(如判断HashMap集合中是否包含某个对象)时,程序就会出现错误,再有就是在比较两个对象时,也一定要重写equals方法,否则程序一样会出错。

下面看一个重写对象的hashCode方法,但是没有重写equals方法,导致程序出错的实例。

例3.21.1 创建类,在该类中重写对象的hashCode方法,但不重写equals方法,然后创建该类的两个实例,将其中一个实例放到HashSet集合中,并判断该集合中是否包含另一个对象,最后再通过对象的equals 方法比较两个对象是否相同。(光盘位置:光盘\MR\Instance\3\21\ RewriteHashCodeApp)

    import java. util. HashSet;
    import java. util. Set;
    public class Book {
      private String bookType;                    // 图书类型
      private String bookName;                    // 图书名称
      public Book (String bookType, String bookName) {
          this. bookType = bookType;               // 初始化图书类型
          this. bookName = bookName;               // 初始化图书名称
      }
      // 重写hashCode方法
    public int hashCode () {
          final int prime = 31;                   // 用于计算散列码的基数
          int result = 1;                         // 存放散列码值的变量
          // 通过图书类型和图书名称计算散列码的值
          result = prime * result
                + ((bookName == null) ? 0 : bookName. hashCode ());
          result = prime * result
                + ((bookType == null) ? 0 : bookType. hashCode ());
          return result;                          // 返回散列码的值
      }
      public static void main (String[] args) {
          Book book1 = new Book ("IT", "Java");    // 创建Book的实例book1
          Book book2 = new Book ("IT", "Java");    // 创建Book的实例book2
          // 输出两个对象的散列码
          System.out. println ("对象book1的散列码是:" + book1. hashCode ());
          System.out. println ("对象book2的散列码是:" + book2. hashCode ());
          // 使用HashSet类创建Set集合对象
    Set<Book> set = new HashSet<Book>();
          set. add (book1);                         // 将Book的实例book1添加到集合
          // 查看集合中是否包含Book的实例book2
    boolean bool = set. contains (book2);
          System.out. println ("Set集合中是否包含对象book2? " + bool);
          System.out. println ("比较对象book1与book2是否相同?"+
    book1. equals (book2));
      }
    }

运行本实例,将在控制台输出如图3.21所示的信息。

图3.21 重写hashCode方法而没重写equals方法

说明

从图3.21的输出结果可以看出,由于重写了对象的hashCode方法,所以前两行输出对象book1和book2的散列码相同,都是71349994,第3行输出false,表示Set集合中不包含对象book2,这是由于在重写hashCode方法的同时,没有重写equals方法,所以导致这样的结果,最后一行也输出了false,说明两个对象book1与book2是不相同的,这也是由于在重写hashCode方法的同时没有重写equals方法所导致的。

下面再看一个重写对象的hashCode方法,同时也重写了equals方法,但是重写equals方法的参数不是Object类型导致程序出错的实例。

例3.21.2 创建类,在该类中重写对象的hashCode方法,同时也重写了equals方法,但是重写equals方法的参数不是Object类型而是Book类型,然后创建该类的两个实例,将其中一个实例放到HashSet集合中,并判断该集合中是否包含另一个对象,最后再通过这个equals 方 法 比 较 两 个 对 象 是 否 相 同。(光 盘 位 置:光 盘 \MR\Instance\3\21\EqualsParameterErrorApp)

运行本实例,将在控制台输出如图3.22所示的信息。

图3.22 重写equals方法,但参数不是Object类型

说明

从图3.22的输出结果可以看出,由于重写了对象的hashCode方法,所以前两行输出对象book1和book2的散列码相同,都是71349994,第3行输出false,表示Set集合中不包含对象book2,这是由于在重写equals方法时,其参数不是Object类型,实际上该方法在这里是重载了对象的equals方法,而不是重写对象的equals方法,所以导致这样的错误结果,最后一行输出了true,说明两个对象book1与book2是相同的,这是由于该类重载的equals方法,其参数是Book类型,因此可以对Book类的实例进行比较。

下面再看一个重写对象的hashCode方法,同时也正确地重写了对象的equals方法,使用程序能够输出正确结果的实例。

例3.21.3 创建类,在该类中重写了对象的hashCode方法和equals方法,然后创建该类的两个实例,将其中一个实例放到HashSet集合中,并判断该集合中是否包含另一个对象,最后再通过equals 方法比较两个对象是否相同。(光盘位置:光盘\MR\Instance\3\21\RightEqualsMethodApp)

运行本实例,将在控制台输出如图3.23所示的信息。

图3.23 正确重写对象的方法输出的结果

说明

本实例之所以输出了正确的结果,是由于程序正确地重写了对象的hashCode 方法和equals方法,所以输出结果的最后两行都输出了true,这样达到了程序预期的要求。

编程准则:一定要同时重写对象的equals和hashCode方法

无论在什么情况下,如果需要重写对象的equals方法和hashCode方法,一定要同时重写这两个方法,不能只重写其中的一个方法,并且在重写对象的equals方法时,一定要注意该方法的形参类型必须要是Object类型的,而不能是其他类型。