【effective Java读书笔记】对于所有对象都通用的方法(一)
来源:互联网 发布:淘宝双11外围流量大吗 编辑:程序博客网 时间:2024/06/05 12:40
一、覆盖equals时遵守通用约定
1)四种情况不需要覆盖equals
//4.类的equals方法是私有的或者是包级私有的public boolean equals(Object o) {//1.类每个实例本质都是唯一的 if (o == this) return true; if (!(o instanceof List)) return false; //2.不关系逻辑相等;3.超类继承过来的行为对于子类也是合适的 ListIterator<E> e1 = listIterator(); ListIterator<?> e2 = ((List<?>) o).listIterator(); while (e1.hasNext() && e2.hasNext()) { E o1 = e1.next(); Object o2 = e2.next(); if (!(o1==null ? o2==null : o1.equals(o2))) return false; } return !(e1.hasNext() || e2.hasNext()); }关于第4点注释:
如果类的equals方法,是私有的(或者包级私有的),则子类的equals方法与父类无关了。
2)覆盖equals方法需要遵守:自反性、对称性、传递性、一致性。
自反性:
对象引用还能不等于它自己么?
对称性:
书中举例:做一个能与字符串String比较的兼容大小写的字符串类型CaseInsensitiveString
public class CaseInsensitiveString {private final String s;public CaseInsensitiveString(String s) {if (s==null) {throw new NullPointerException();}this.s = s;}@Overridepublic boolean equals(Object obj) {if (obj instanceof CaseInsensitiveString) {//属于当前对象类型时return s.equalsIgnoreCase(((CaseInsensitiveString) obj).s);}if (obj instanceof String) {//注意:兼容String类型return s.equalsIgnoreCase((String) obj);}return false;}}执行测试代码:
@org.junit.Testpublic void test2() {CaseInsensitiveString cis = new CaseInsensitiveString("Polish");String s = "polish";System.out.println(cis.equals(s));System.out.println(s.equals(cis));}执行结果:
true
false
这样就不符合对称性,A等于B时,B不等于A这样子就很怪异。解决方案:不兼容String类型。
@Overridepublic boolean equals(Object obj) {if (obj instanceof CaseInsensitiveString) {//属于当前对象类型时return s.equalsIgnoreCase(((CaseInsensitiveString) obj).s);}return false;}
传递性:
和上一个例子比较相似的一点是,也是因为强行兼容导致的灾难。区别,在于一个父子类继承关系。
父类:
public class Point {private int x;private int y;public Point(int x,int y) {this.x = x;this.y = y;}@Overridepublic boolean equals(Object obj) {if (!(obj instanceof Point)) {return false;}Point p = (Point) obj;return p.x==x && p.y==y;}}子类:
public class ColorPoint extends Point {private Color color;public ColorPoint(int x, int y,Color color) {super(x, y);this.color = color;}@Overridepublic boolean equals(Object obj) {// if (!(obj instanceof ColorPoint)) {return false;}return super.equals(obj)&&((ColorPoint)obj).color==color;}}看着毫无争议的写法。但是对称性考虑的时候,会发现:
@org.junit.Testpublic void test4(){Point p = new Point(1, 2);ColorPoint cp = new ColorPoint(1, 2, Color.RED);System.out.println(p.equals(cp));//违反了对称性System.out.println(cp.equals(p));}执行结果:
true
false
但是,对于这个例子,和上一个不一样的地方在于,我们可以对ColorPoint这个子类做兼容处理@Overridepublic boolean equals(Object obj) {if (!(obj instanceof Point)) {return false;}//如果被比较对象是Point但不是ColorPoint类型的话,则尝试返回反向比较:调用Point的equals方法,兼容Pointif (!(obj instanceof ColorPoint)) {return obj.equals(this);}return super.equals(obj)&&((ColorPoint)obj).color==color;}想出这个反向比较的方案也算是很另类了。执行结果如下:
true
true
就在以为就此结束的时候,发现传递性没有了。看测试代码:@org.junit.Testpublic void test4(){ColorPoint p1 = new ColorPoint(1, 2, Color.RED);Point p2 = new Point(1, 2);ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);System.out.println("p1等于p2----"+p1.equals(p2));System.out.println("p2等于p3----"+p2.equals(p3));System.out.println("p1等于p3----"+p1.equals(p3));}执行结果:
p1等于p2----true
p2等于p3----true
p1等于p3----false
p1和p3由于都是ColorPoint所以会执行returnsuper.equals(obj)&&((ColorPoint)obj).color==color;
所以当然不一样false。结论:我们无法在扩展可实例化的类的同时,既增加值的组件,同时又保留equals约定。
所以,不要为了兼容其他类型(包括父类,子类型)去试图覆盖equals方法。这种情况,使用统一的父类的equals方法能处理是最好,例如abstarctList,abstarctMap。
解决方案一:
书中提及这样一种方案:(书中不推荐的方案)只有相同实现才去比较。例如上例子中,只有Point和Point比较,ColorPoint和ColorPoint比较。Point和ColorPoint比较则始终为false。
public class Point2 {private int x;private int y;public Point2(int x,int y) {this.x = x;this.y = y;}@Overridepublic boolean equals(Object obj) {System.out.println("obj.getClass()------"+obj.getClass());System.out.println("getClass()------"+getClass());//相同实现才可以比较if (obj==null||obj.getClass()!=getClass()) {return false;}Point2 p = (Point2) obj;return p.x==x && p.y==y;}}
然而这种不兼容的方案,从业务的角度上却又是不符合原则的。例如:
一个Point点集合:
public class UnitCircle {private static final Set<Point2> unitCircle;static{unitCircle = new HashSet<Point2>();unitCircle.add(new Point2(1, 0));unitCircle.add(new Point2(0, 1));unitCircle.add(new Point2(-1, 0));unitCircle.add(new Point2(0, -1));}public static boolean onUnitCircle(Point2 p){return unitCircle.contains(p);}}
一个Point子类:仅仅加了一个计数的值。直接使用父类的equals方法,却都无法正常比较使用。
public class CounterPoint extends Point2 {private static final AtomicInteger counter = new AtomicInteger();public CounterPoint(int x, int y) {super(x, y);counter.incrementAndGet();}public int numberCreated(){return counter.get();}}测试代码:
@org.junit.Testpublic void test6(){CounterPoint cPoint = new CounterPoint(1, 0);System.out.println(UnitCircle.onUnitCircle(cPoint));}执行结果:
false
因为两个类Point 和 ConuterPoint实现类不是一样的,所以始终都是false。
解决方案二:
利用复合,提供视图去比较:
//1.不再继承Pointpublic class ColorPoint3{private Color color;//2.写一个Point对象引用private Point point;public ColorPoint3(int x, int y,Color color) {if (color==null) {throw new NullPointerException();}point = new Point(x, y);this.color = color;}//3.对外公开一个point视图public Point getPoint() {return point;}@Overridepublic boolean equals(Object obj) {if (!(obj instanceof ColorPoint3)) {return false;}ColorPoint3 cp = (ColorPoint3) obj;return cp.point.equals(point)&&cp.color.equals(color);}}执行代码:
@org.junit.Testpublic void test8(){ColorPoint3 cPoint = new ColorPoint3(1, 0,Color.RED);Point p = new Point(1, 0);System.out.println(cPoint.getPoint().equals(p));System.out.println(p.equals(cPoint.getPoint()));}执行结果:
true
true
结论:
1、直接通过继承使用父类的equals方法。
2、使用复合处理;
3、使父类为抽象类,父类不能实例化。完全都通过之类的equals方法比较。
一致性:
针对引用型对象:如果两个对象相等,则需要一直相等。- 【effective Java读书笔记】对于所有对象都通用的方法(一)
- Effective Java读书笔记(第3章-对于所有对象都通用的方法)
- 【读书笔记】《Effective Java》(2)--对于所有对象都通用的方法
- 《Effective Java》读书笔记(二)之对于所有对象都通用的方法
- 【effective Java读书笔记】对于所有对象都通用的方法(二)
- 【effective Java读书笔记】对于所有对象都通用的方法(三)
- Effective Java 读书笔记(二):对于所有对象都通用的方法
- Effective Java读书笔记(3对于所有对象都通用的方法)
- effective java-读书笔记-第三章 对于所有对象都通用的方法
- Effective Java读书笔记——第三章 对于所有对象都通用的方法
- effective java 读书笔记---第三章对于所有对象都通用的方法
- Effective Java:对于所有的对象都通用的方法
- effective java(对于所有对象都通用的方法)
- Effective Java:对于所有对象都通用的方法
- Effective Java Note(对于所有对象都通用的方法)
- Effective Java:对于所有对象都通用的方法
- Effective java笔记-对于所有对象都通用的方法
- Effective Java(二) 对于所有对象都通用的方法
- crontab执行sh脚本Java部分不成功
- 浅谈Java反射与Annotation
- Java邮箱正则表达式
- 生成1~n的排列 生成可重集的排列 下一个排列
- Python闭包
- 【effective Java读书笔记】对于所有对象都通用的方法(一)
- PostgreSQL存储过程循环调用
- 使用阿里云消息服务mns发送短信
- java连接hbase1.2.6
- AFNetworking打印输出服务器返回的错误信息
- 常见软件开发模型简述版
- Oracle 数据库 练习题 T8
- Maven入门2-pom.xml文件与settings.xml文件
- python 操作mysql