您的位置:首页 > 编程语言 > Java开发

Java相等性测试

2015-08-04 18:09 369 查看
最近在看《Java核心技术卷I》时有看到相等性测试的相关内容,就一些学习心得做一下分享。

1.Object类中的equals方法

Object类是java中所有类的始祖,Object类型的变量可以引用任何类型的对象。java中只有基本类型不是对象,所有的数组类型不管是对象数组还是基本类型数组都扩展于Object类。Object类中的equals方法用于判断两个对象是否具有相同的引用,如果两个对象具有相同的引用,它们一定是相等的。但对于多数类来说,这种判断没有什么意义。大多数时候需要检测的是对象的状态是否相等。

2.重写equals方法

以下实现了重写equals方法的Employee类和Manager类。

class Employee {
private static int nextId = 1;

private String name;
private double salary;
private int id;
private Date hireDay;

public Employee(String n, double s, int year, int month, int day) {
name = n;
salary = s;
id = 0;
GregorianCalendar calendar = new GregorianCalendar(year, month-1 ,day);
hireDay = calendar.getTime();
}

public boolean equals(Object otherObject) {
//a quick test to see if the objects are identify
if (this == otherObject) return true;
//must return false if the explicit parameter is null
if (otherObject == null) return false;
//if the classes don't match, they can't be equal
if (getClass() != otherObject.getClass()) return false;
//now we know otherObject is a non-null Employee
Employee other = (Employee) otherObject;
//test whether the fields have identical values
return Objects.equals(name,other.name)
&& salary == other.salary
&& Objects.equals(hireDay,other.hireDay);

}
}
</pre><span style="white-space:pre"> </span>注意Employee.equals方法的最后一条语句,如果改为 :<p></p><p></p><pre name="code" class="cpp">return name.equals(other.name)
&& salary == other.salary
&& hireDay.equals(other.hireDay);
则如果name或者hireDay为null会报异常。

子类中定义equals方法时,首先调用超累的equals。如果超类检测失败,对象就不可能相等。如果超类中的域相等则需要比较子类中的实例域。

class Manager extends Employee {
private double bonus;
public Manager(String n, double s, int year, int month, int day) {
super(n,s,year,month,day);
bonus = 0;
}

public double getSalary() {
double baseSalary = super.getSalary();
return baseSalary + bonus;
}

public void setBonus(double b) {
bonus = b;
}

public boolean equals(Object otherObject) {
if(!super.equals(otherObject))
return false;
Manager other = (Manager) otherObject;
//super.equals checked that this and other belong to the same class
return bonus == other.bonus;
}
}


3.相等性测试特点

(1)自反性:对于任何非空引用x,x.equals(x)应该返回true.

(2)对称性:对于任何引用x和y,x.equals(y)和y.equals(x)都应该返回true.

(3)传递性:对于任何引用x/yz,如果x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)也应该返回true.

(4)一致性:如果x、y没有发生变化,反复调用x.equals(y)应返回同样结果.

(5)对于任意非空引用x,x.equals(null)应返回false.

(6)如果x.equals(y)返回true,则必须有x.hashCode()==y.hashCode().

4.继承中的相等性测试

就对称性来说,当参数不属于同一个类的时候需要十分注意。比如:

Employee e = new Employee("a",100,2000,10,10);
Manager m = new Manager("a",100,2000,10,10);
当Employee类的equals方法中的第三个判断使用intanceof进行检测,则e.equals(m)返回true。而m.equals(e)返回false,不满足对称性。这是因为:

if (m instanceof Employee)
System.out.println("子类对象是父类的实例");//子类是父类的实例
if (!(e instanceof Manager))
System.out.println("父类对象不是子类的实例");//父类对象不是子类的实例
如果使用getClass()来进行检测:

if (e.getClass() != m.getClass())
System.out.println(" 超类.getClass() != 子类.getClass()"); //超类.getClass() != 子类.getClass()
此时e.equals(m)和m.equals(e)都返回false,满足对称性。此时只要是子类与超类测试相等性则都会返回false。

在继承关系中的相等性测试,应该根据语义分两种情况考虑:

(1)如果子类能够拥有自己的相等概念,则对称性需求将强制采用getClass进行检测。

(2)如果由超类决定相等的概念,那么就可以使用instanceof进行检测,这样可以在不同子类的对象之间进行相等的比较。

对于(1)的情况:比如两个对象所对应的名字,薪水和雇佣日期相等而奖金不相等,则认为它们是不同的。

对于(2)的情况,假设使用name作为相等的检测标准,并且这个相等的概念适用于所有子类,就可以使用instanceof进行检测,并且应该将超类的equals方法声明为final。

5.重写完美的equals

(1)显示参数参数命名为otherObject,稍后需要将它转换成另一个other的变量;

(2)检测this与otherObject是否引用同一个对象;

(3)检测otherObject是否为null;

(4)比较this与otherObject是否属于同一个类。如果equals的语义在每个子类中有所改变,就使用getClass检测:

if(getClass() != otherObject.getClass())
return false;
如果所有子类都拥有统一的语义,就使用instanceof检测:

if(!(otherObject  instanceof ClassName))
return false;
(5)将otherObject转换为相应的类类型变量(this类类型)

(6)比较实例域,使用==比较基本类型域,使用equals比较对象域.

(7)如果在子类中重新定义equals,首先测试超类的相等性.

public class StaticTest {
public static void main(String[] args) {
Employee a = new Employee("a",100,2000,10,10);
Employee b = a;
Employee c = new Employee("a",100,2000,10,10);
Employee d = new Employee("d",200,2001,5,5);

System.out.println("a == b:" + (a == b));//true
System.out.println("a == c:" + (a == c));//false,"=="对比栈上的对象变量
System.out.println("a.equals(c):" + (a.equals(c)));//true,equals()方法对比堆中的对象成员
System.out.println("a.equals(d):" + a.equals(d));//false
System.out.println("d.toString():" + d);//只要对象与一个字符串通过操作符"+"连接起来,java编译器就会自动调用toString();

Manager carl = new Manager("Carl",800,1990,12,15);
Manager boss = new Manager("Carl",800,1990,12,15);
boss.setBonus(500);
System.out.println("boss.toString():" + boss);
System.out.println("carl.equals(boss):" + carl.equals(boss));

if (carl instanceof Employee)
System.out.println("子类对象是父类的实例");//子类是父类的实例
if (!(a instanceof Manager))
System.out.println("父类对象不是子类的实例");//子类是父类的实例
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: