我们都知道在Java规范里定义了equals方法覆盖的5大原则:reflexive(反身性),symmetric(对称性),transitive(传递性),consistent(一致性),non-null(非空性)。那么考察下面的代码:
 public class Student{
   private String name;
   private int age;
   public Student(String name,int age){
       this.name=name;
       this.age=age;
   }
   public boolean equals(Object obj){
       if(obj instanceof Student){
           Student s=(Student)obj;
           if(s.name.equals(this.name) && s.age==this.age){
               return true;
           }
       }
       return super.equals(obj);
   }
}
你认为上面的代码equals方法的覆盖安全吗?表面看起来好像没什么问题,这样写也确实满足了以上的五大原则。但其实这样的覆盖并不很安全,假如Student类还有一个子类CollegeStudent,如果我拿一个Student对象和一个CollegeStudent对象equals,只要这两个对象有相同的name和age,它们就会被认为相等,但实际上它们是两个不同类型的对象啊。问题就出在instanceof这个运算符上,因为这个运算符是向下兼容的,也就是说一个CollegeStudent对象也被认为是一个Student的实例。怎样去解决这个问题呢?

解决方案 »

  1.   

    我觉的是is-a的关系吧
    既然 CollegeStudent 继承Student ;
    就说明 子类是父类 此时转化当然没问题了吧。
    不知道能解释这个问题不?
      

  2.   

    把hashCode一并重写。或者实现 Compareable<T>接口。
      

  3.   

    equals方法中有这样一句:
    注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
      

  4.   

    你可以把
    if(obj instanceof Student)
    修改为if(obj != null && obj.getClass() == Student.class)
    ...
    话说我一直认为Java里面的equals是很差的设计,每个类都重复判断
      

  5.   

    这个是我在core java这本书上摘抄的,供参考!!
    Here is a recipe for writing the perfect equals method:
    1. Name the explicit parameter otherObject—later, you need to cast it to another variable
    that you should call other.
    2. Test whether this happens to be identical to otherObject:
    if (this == otherObject) return true;
    This statement is just an optimization. In practice, this is a common case. It is much
    cheaper to check for identity than to compare the fields.
    3. Test whether otherObject is null and return false if it is. This test is required.
    if (otherObject == null) return false;
    4. Compare the classes of this and otherObject. If the semantics of equals can change in
    subclasses, use the  getClass test:
    if (getClass() != otherObject.getClass()) return false;
    If the same semantics holds for all subclasses, you can use an  instanceof test:
    if (!(otherObject instanceof ClassName)) return false;
    5. Cast otherObject to a variable of your class type:
    ClassName other = (ClassName) otherObject
    6. Now compare the fields, as required by your notion of equality. Use  == for primitive
    type fields,  equals for object fields. Return true if all fields match, false otherwise.
    return field1 == other.field1
       && field2.equals(other.field2)
       && . . .;
    If you redefine equals in a subclass, include a call to super.equals(other).
      

  6.   

      public boolean equals(Object obj){
      if(obj instanceof Student){
      Student s=(Student)obj;
      // 如果s.name == null,下面就报销了
      if(s.name.equals(this.name) && s.age==this.age){
      return true;
      }
      }
      return super.equals(obj);
      }这是一个问题。在扩展性上仍然有潜在问题
    假设
    public class XStudent extends Student {
      int grade;
      // 构造函数也省略,你懂的
      public boolean equals(Object obj) {
        // 类似上面,主要是判断是否是XStudent实例,并且多了一个grade的判断,不写了
      }
    }假设
    Student s1 = new Student("张三", 10);
    Student s2 = new XStudent("张三", 10, 5);
    此时s1.equals(s2) 与 s2.equals(s1),无论XStudent的equals怎么调整,都不符合symmetric原则。
      

  7.   

    无论XStudent的equals怎么调整,都不符合symmetric原则。
    ---
    这个是可以的,写法都是使用if(obj != null && obj.getClass() == getClass())就可以
      

  8.   

    这个是写在Student里面的。我是说Student.equals不变的情况下,XStudent.equals徒呼奈何
      

  9.   

    要重写hashcode,原因只是维护一个准则的正确性,如下:
    equals相等的两个值,其hashcode一定相等,
    equals不等的两个值,其hashcode不一定相等。
    hashcode相等的两个值,其equals不一定相等,
    hashcode不等的两个值,其equals一定不相等。再来看,如果你重写了object的equals是两个值相等了,但是hashcode并无重写,导致hashcode不等,岂不是很奇怪?可以自己举例试试。
      

  10.   

    这个是正确答案...分自然可以拿走...  不过重写hashcode也是可以实现的...
      

  11.   

    将equals()方法修改为:
      public boolean equals(Object obj){ 
           if(obj.getClass()==Student.class){
               Student s=(Student)obj;
               if(s.name.equals(this.name) && s.age==this.age){
                   return true;
               }
           }
           return super.equals(obj);
       }
     这样才能保证obj对象一定是Student的实例,而不会是Student的任何子类的实例。
      

  12.   

    equals
    public boolean equals(Object obj)指示某个其他对象是否与此对象“相等”。 
    equals 方法在非空对象引用上实现相等关系: 自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。 
    对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。 
    传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。 
    一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。 
    对于任何非空引用值 x,x.equals(null) 都应返回 false。 
    Object 类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true(x == y 具有值 true)。 注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。