编写高质量代码:改善Java程序的151个建议(15)

  建议47:在equals中使用getClass进行类型判断

  本节我们继续讨论覆写equals的问题。这次我们编写一个员工Employee类继承Person类,这很正常,员工也是人嘛,而且在JEE中JavaBean有继承关系也很常见,代码如下:

  

    classEmployeeextendsPerson{ privateintid; /*id的getter/setter方法省略*/ publicEmployee(String_name,int_id){ super(_name); id=_id; } @Override publicbooleanequals(Objectobj){ if(objinstanceofEmployee){ Employeee=(Employee)obj; returnsuper.equals(obj)&&e.getId()==id; } returnfalse; } }

  员工类增加了工号ID属性,同时也覆写了equals方法,只有在姓名和ID号都相同的情况下才表示是同一个员工,这是为了避免在一个公司中出现同名同姓员工的情况。看看上面的代码,这里校验条件已经相当完备了,应该不会再出错了,那我们编写一个main方法来看看,代码如下:

  

    publicstaticvoidmain(String[]args){ Employeee1=newEmployee(“张三”,100); Employeee2=newEmployee(“张三”,1001); Personp1=newPerson(“张三”); System.out.println(p1.equals(e1)); System.out.println(p1.equals(e2)); System.out.println(e1.equals(e2)); }

  上面定义了2个员工和1个社会闲杂人员,虽然他们同名同姓,但肯定不是同一个,输出应该都是false,那我们看看运行结果:

  

    true true false

  很不给力嘛,p1竟然等于e1,也等于e2,为什么不是同一个类的两个实例竟然也会相等呢?这很简单,因为p1.equals(e1) 是调用父类Person的equals方法进行判断的,它使用instanceof关键字检查e1是否是Person的实例,由于两者存在继承关系,那结果当然是true了,相等也就没有任何问题了,但是反过来就不成立了,e1或e2可不等于p1,这也是违反对称性原则的一个典型案例。

  更玄的是p1与e1、e2相等,但e1竟然与e2不相等,似乎一个简单的等号传递都不能实现。这才是我们要分析的真正重点:e1.equals(e2)调用的是子类Employee的equals方法,不仅仅要判断姓名相同,还要判断工号是否相同,两者工号是不同的,不相等也是自然的了。等式不传递是因为违反了equals的传递性原则,传递性原则是指对于实例对象x、y、z来说,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true。

  这种情况发生的关键是父类使用了instanceof关键字,它是用来判断是否是一个类的实例对象的,这很容易让子类“钻空子”。想要解决也很简单,使用getClass来代替instanceof进行类型判断,Person类的equals方法修改后如下所示:

  

    publicbooleanequals(Objectobj){ if(obj!=null&&obj.getClass()==this.getClass()){ Personp=(Person)obj; if(p.getName()==null||name==null){ returnfalse; }else{ returnname.equalsIgnoreCase(p.getName()); } } returnfalse; }

  当然,考虑到Employee也有可能被继承,也需要把它的instanceof修改为getClass。总之,在覆写equals时建议使用getClass进行类型判断,而不要使用instanceof。

如果雨后还是雨,如果忧伤过后还是忧伤,

编写高质量代码:改善Java程序的151个建议(15)

相关文章:

你感兴趣的文章:

标签云: