标签:
Java中的所有类都继承自Object类,Object类中有许多通用的方法,这一章要讨论的是:对于Object类中的通用方法,我们的类要不要继承,以及继承时需要注意的事项。
首先看一下不需要覆盖的情况:
1.类的每个实例本质上是唯一的。(比如Static的,单例的等等),这样不需要特意覆盖equals方法,用Object类的equals()方法就足够了
2.不关心类是否实现了“逻辑相等”的测试功能。我们用equals的目的就是判断两个对象是否是“逻辑相等”的,比如String类的值是否相等。如果这个类并不需要这个功能,那么我们自然没必要覆盖equals()方法。
3.超类已经覆盖了equals()方法,且从超类继承过来的行为对子类也是合适的。
4.有一种“值类”不需要覆盖,即实例受控类,确保“每个值之多只存在一个对象”,枚举类型就是这种类,对于这样的类,逻辑相等与对象相等是一回事,所以不需要覆盖equals()方法
那么什么时候需要覆盖呢?
如果类有自己特有的“逻辑相等”的概念,而且父类还没有覆盖equals以实现期望的行为,这时我们就需要覆盖equals()方法了。这通常属于“值类”。值类通常是一个只表示值的类,如Integer,程序员在用equals比较时仅仅希望知道它们的值是否相等,而关心它们是否指向同一个对象。
equals方法需要实现“等价关系”,包含四个方面:
1.自反性。x.equals(x)必须始终返回true.
2.对称性。如果x.equals(y)=true,那么y.equals(x)也必须为true.
3.传递性。如果有x.equals(y)= true,y.equals(z) = true,那么也必有x.quals(z) = true.
4.一致性。只要值没被修改,那么多次调用x.equals(y)的值应该始终相等。
下面举一些违反了这4条的例子。
违反自反性:这种一般很少,因为这一条仅仅要求对象等于自身。如果违反了的话,假设你把一个对象加到一个集合里,然后查询,该集合的contains方法会告诉你集合中不存在这个元素。
违反对称性:考虑下面的类,它实现了一个区分大小写的字符串,但比较时不考虑大小写。
public final class CaseInsensitiveString{ private final String s; ... public boolean equals(Object o){ if( o instanceof CaseInsentiveString){ return s.equalsIgnoreCase( ((CaseInsentiveString) o).s); if( o instanceof String) //考虑了与普通String类的比较 return s.equalsIgnoreCase((String) o); return false; } }
看上去考虑的很好,比较时,考虑了传入的对象是本类和String类的情况。但是,假设我们有两个对象:
CaseInsensitiveString cis = new CaseIntensitiveString("Polish");
String s = "polish";
那么,显然cis.equals(s)=true。但是问题来了,s.equals(cis) = false。这就违反了对称性。
解决方法是,如果A类的equals方法中引入了对B类对象的比较,那么B类反过来也一定要引入对A类的比较。
违反传递性:违反这条一般是因为继承时子类添加了新的值对象。子类添加的信息会影响equals的比较结果。
比如我们有一个简单的Point类:
public class Point{ private final int x; private final int y; public Point(int x,int y){ this.x = x; this.y = y; } public boolean equals(Object o){ if(!(o instanceof Point)) return false; Point p = (Point) o; return this.x == p.x && this.y == p.y; } }
假设你想扩展这个类,为每个点加一个颜色信息:
public class ColorPoint extends Point{ private final Color color; public ColorPoint(int x,int y ,Color c){ super(x,y); color = c; } }
equals方法是怎样呢?有两种考虑:1.只和有色点比较,要x,y,color都相等才返回true,对于不是有色点的对象,返回结果始终是false
2.有色点也能够和普通点比较,如果和普通点比较,就比较x,y的值。
看第一种方法的实现:
public boolean equals(Object o){ if(!(o instance of ColorPoint)){ return false; return super.equalso) && ((ColorPoint) o).color == color; } }
如果有一个有色点对象cp(1,2,red)和一个普通点对象p(1,2),那么p.equals(cp) = true,cp.equals(p) = false。违反了对称性。
第二种方法:
public boolean equals(Object o){ if(!(o instanceof Point)) return false; if(!(o instanceof ColorPoint)) //如果对象不是colorPoint类的话,就用这个对象的equals方法来判断 return o.equals(this); return super.equalso) && ((ColorPoint) o ).color = color; }
这种方法实现了对称性,但是牺牲了传递性。考虑有色点cp1(1,2,red),cp2(1,2,blue)和普通点p(1,2)
有cp1.equals(p) = true ,p.equals(cp2) ,但是cp1.equals(cp2)。违反了传递性。
我们无法在扩展可实例化的类的同时,既增加新的值组件,又保留equals约定。
怎么解决呢?
复合优先于继承。
我们不再继承Point类,而是新建一个ColorPoint类,在其中加入一个Point类的引用。
今天先写到这,明天继续
effective java读书笔记——对于所有对象都通用的方法
标签:
原文地址:http://www.cnblogs.com/cangyikeguiyuan/p/4388292.html