通过打印结果我们发现 person.equals(man)得到的结果是 true,而man.equals(person)得到的结果却是false,这显然是不正确的。
问题出现在 instanceof 关键字上,关于 instanceof 关键字的用法,可以参考我的这篇文章:http://www.cnblogs.com/ysocean/p/8486500.html
Man 是 Person 的子类,person instanceof Man 结果当然是false。这违反了我们上面说的对称性。
实际上用 instanceof 关键字是做不到对称性的要求的。这里推荐做法是用 getClass()方法取代 instanceof 运算符。getClass() 关键字也是 Object 类中的一个方法,作用是返回一个对象的运行时类,下面我们会详细讲解。
那么 Person 类中的 equals 方法为
打印结果 person.equals(man)得到的结果是 false,man.equals(person)得到的结果也是false,满足对称性。
注意:使用 getClass 不是绝对的,要根据情况而定,毕竟定义对象是否相等的标准是由程序员自己定义的。而且使用 getClass 不符合多态的定义,比如 AbstractSet 抽象类,它有两个子类 TreeSet 和 HashSet,他们分别使用不同的算法实现查找集合的操作,但无论集合采用哪种方式实现,都需要拥有对两个集合进行比较的功能,如果使用 getClass 实现equals方法的重写,那么就不能在两个不同子类的对象进行相等的比较。而且集合类比较特殊,其子类是不需要自定义相等的概念的。
所以什么时候使用 instanceof 运算符,什么时候使用 getClass() 有如下建议:
①、如果子类能够拥有自己的相等概念,则对称性需求将强制采用 getClass 进行检测。
②、如果有超类决定相等的概念,那么就可以使用 instanceof 进行检测,这样可以在不同的子类的对象之间进行相等的比较。
下面给出一个完美的 equals 方法的建议:
1、显示参数命名为 otherObject,稍后会将它转换成另一个叫做 other 的变量。
2、判断比较的两个对象引用是否相等,如果引用相等那么表示是同一个对象,那么当然相等
3、如果 otherObject 为 null,直接返回false,表示不相等
4、比较 this 和 otherObject 是否是同一个类:如果 equals 的语义在每个子类中有所改变,就使用 getClass 检测;如果所有的子类都有统一的定义,那么使用 instanceof 检测
5、将 otherObject 转换成对应类的类型变量
6、最后对对象的属性进行比较。使用 == 比较基本类型,使用 equals 比较对象。如果都相等则返回true,否则返回false。注意如果是在子类中定义equals,则要包含 super.equals(other)
下面我们给出 Person 类中完整的 equals 方法的书写:
请注意,无论何时重写此方法,通常都必须重写hashCode方法,以维护hashCode方法的一般约定,该方法声明相等对象必须具有相同的哈希代码。hashCode 也是 Object 类中的方法,后面会详细讲解。
5、getClass 方法
上面我们在介绍 equals 方法时,介绍如果 equals 的语义在每个子类中有所改变,那么使用 getClass 检测,为什么这样说呢?
getClass()在 Object 类中如下,作用是返回对象的运行时类。
这是一个用 native 关键字修饰的方法,关于 native 关键字的详细介绍如下:http://www.cnblogs.com/ysocean/p/8476933.html
这里我们要知道用 native 修饰的方法我们不用考虑,由操作系统帮我们实现,该方法的作用是返回一个对象的运行时类,通过这个类对象我们可以获取该运行时类的相关属性和方法。也就是Java中的反射,各种通用的框架都是利用反射来实现的,这里我们不做详细的描述。
这里详细的介绍 getClass 方法返回的是一个对象的运行时类对象,这该怎么理解呢?Java中还有一种这样的用法,通过 类名.class 获取这个类的类对象 ,这两种用法有什么区别呢?
父类:Parent.class
子类:Son.class
测试:
打印结果:
结论:class 是一个类的属性,能获取该类编译时的类对象,而 getClass() 是一个类的方法,它是获取该类运行时的类对象。