List的contains()方法详解以及重写equals()方法时需要注意的地方 |
您所在的位置:网站首页 › java重载equals › List的contains()方法详解以及重写equals()方法时需要注意的地方 |
文章摘抄自http://blog.csdn.net/javazejian/article/details/51348320 我们先来看看contains方法内部实现 @Override public boolean contains(Object o) { return indexOf(o) != -1; }进入indexof方法 @Override public int indexOf(Object o) { E[] a = this.a; //当o是null的时候也会返回集合中null的位置 if (o == null) { for (int i = 0; i < a.length; i++) if (a[i] == null) return i; } else { //indexOf(),实际调用的是传入的contains(Object o)的实参的equals方法 for (int i = 0; i < a.length; i++) if (o.equals(a[i])) return i; } return -1;一般情况下我们没有重写equals()方法,这时就会调用继承自object类的equals()方法,而我们都知道object的equals()方法,实际就是 ==,如下 public boolean equals(Object obj) { return (this == obj); }比较的是是否为同一个对象。所以再调用contains()方法时,实际就是看是否是同一个对象,但是如果重写了contains(Object o)中形参的equals()方法,那么就会产生不同的效果。 而大家都知道,如果重写equals方法,就必须要遵循以下规则: 自反性。对于任何非null的引用值x,x.equals(x)应返回true。 对称性。对于任何非null的引用值x与y,当且仅当:y.equals(x)返回true时,x.equals(y)才返回true。 传递性。对于任何非null的引用值x、y与z,如果y.equals(x)返回true,y.equals(z)返回true,那么x.equals(z)也应返回true。 一致性。对于任何非null的引用值x与y,假设对象上equals比较中的信息没有被修改,则多次调用x.equals(y)始终返回true或者始终返回false。 对于任何非空引用值x,x.equal(null)应返回false。 下面举个例子: import java.util.ArrayList; import java.util.List; public class AbnormalResult { public static void main(String[] args) { List list = new ArrayList(); A a = new A(); B b = new B(); list.add(a); System.out.println("list.contains(a)->" + list.contains(a)); System.out.println("list.contains(b)->" + list.contains(b)); list.clear(); list.add(b); System.out.println("list.contains(a)->" + list.contains(a)); System.out.println("list.contains(b)->" + list.contains(b)); } //我们通过静态内部类来精简代码,可以看到我们都重写了List中插入元素的equals方法,这样在调用contains方法时,就会按照我们的equals方法进行操作 static class A { @Override public boolean equals(Object obj) { return obj instanceof A; } } static class B extends A { @Override public boolean equals(Object obj) { return obj instanceof B; } } }我们可以看到输出结果为: list.contains(a)->true list.contains(b)->false list.contains(a)->true list.contains(b)->true 表面上看来这样没有问题,但是如果我们只观察这个equals方法就会发现,它违反了重写equals()方法的对称性原则,因为上面调用的四次contains()方法,实际调用的equals()方法如下所示: ①a.equals(a)->true;//因为我们只插入了一个元素,所以就是在跟插入的这个元素比较,下同 ②b.equals(a)->false; ③a.equals(b)->true; ④b.equals(b)->true; 可以看到②③的比较的结果是不同的,这就违反了对称性原则。所以,当你打算重写contains()方法的equals()规则时,一定要检查清楚,是否符合以上原则,验证后再进行使用。 上面的代码改为如下,就会都返回true static class B extends A{ @Override public boolean equals(Object obj) { if(obj instanceof B){ return true; } return super.equals(obj); } }但实际上这种方法又会违背传递性原则,比如下面一个例子 //父类 public class Person { protected String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Person(String name){ this.name = name; } public boolean equals(Object object){ if(object instanceof Person){ Person p = (Person) object; if(p.getName() == null || name == null){ return false; } else{ return name.equalsIgnoreCase(p.getName ()); } } return false; } } //子类 public class Employee extends Person{ private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } public Employee(String name,int id){ super(name); this.id = id; } /** * 重写equals()方法 */ public boolean equals(Object object){ if(object instanceof Employee){ Employee e = (Employee) object; return super.equals(object) && e.getId() == id; } return super.equals(object); } //测试代码 public class Test { public static void main(String[] args) { Employee e1 = new Employee("chenssy", 23); Employee e2 = new Employee("chenssy", 24); Person p1 = new Person("chenssy"); System.out.println(p1.equals(e1)); System.out.println(p1.equals(e2)); System.out.println(e1.equals(e2)); } }根据传递性原则,e1.equals(e2)也应该返回true,但实际结果是false,对于那 e1!=e2 我们非常容易理解,因为他们不仅需要比较 name,还需要比较 ID。但是 p1 即等于 e1 也等于 e2,这是非常奇怪的,因为 e1、e2 明明是两个不同的类,但为什么会出现这个情况?首先 p1.equals(e1),是调用 p1 的 equals 方法,该方法使用 instanceof 关键字来检查 e1 是否为 Person 类,这里我们再看看 instanceof:判断其左边对象是否为其右边类的实例,也可以用来判断继承中的子类的实例是否为父类的实现。他们两者存在继承关系,肯定会返回 true 了,而两者 name 又相同,所以结果肯定是 true。所以出现上面的情况就是使用了关键字 instanceof,这是非常容易导致我们“钻牛角尖”。故在覆写 equals 时推荐使用 getClass 进行类型判断。而不是使用 instanceof(除非子类拥有统一的语义) 因为getclass()方法判断的是是否为同一个类,而instanceof()判断是否有继承关系,如果上面equals(),里面的instanceof()换成object.getClass()==Employee.class,就不会出现上述问题,因为都返回false。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |