陷阱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类型的,而不能是其他类型。