effective java摘抄(一)
2017-04-12 14:09
330 查看
一、覆盖equals请遵守通用约定
1、覆盖equals的约定
自反性:对于任何非null的引用值x,x.equals(x)必须返回true。
对称性:对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。
传递性:对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)返回true,那么x.equals(z)也必须返回true。
一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致地返回true,或者一致的返回false。
对于任何非null的引用值x,x.equals(null)必须返回false。
2、违反对称性的案例:
public class CaseInsensitiveString {
private final String s;
public CaseInsensitiveString(String s){
if(s==null)
throw new NullPointerException();
this.s = s;
}
@Override
public boolean equals(Object o) {
if(o instanceof CaseInsensitiveString){
return s.equalsIgnoreCase(((CaseInsensitiveString) o).s);
}
if(o instanceof String)
return s.equalsIgnoreCase((String)o);
return false;
}
}
对于以上代码:如果假设现在有两个字符串,一个不区分大小写的,另一个是普通字符串
CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
String s = "polish";
cis.equals(s); //返回true
s.equals(cis); //返回false
虽然CaseInsensitiveString中的equals知道普通的字符串对象,但是String类中的equals方法并不知道不区分大小写的字符串,因此s.equals(cis)返回false。这就违反了对称性
3、违反对称性或传递性的案例:
public class Point {
private final int x;
private final int y;
public Point(int x,int y){
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if(!(o instanceof Point)) return false;
Point p = (Point)o;
return p.x == x && p.y == y;
}
}
现在扩展当前类,为点增加颜色属性
public class ColorPoint extends Point{
private final Color color;
public ColorPoint(int x,int y,Color color){
super(x,y);
this.color = color;
}
@Override
public boolean equals(Object o) {
if(!(o instanceof ColorPoint)) return false;
return super.equals(o) && ((ColorPoint)o).color == color;
}
}
这个问题在于,比较普通点和有色点,以及有色点和普通点反过来对比时可能会得到不同的结果。普通点和有色点比较时忽略了颜色信息,但有色点和普通点比较时永远返回false因为参数的类型不正确。这违反了对称性,改进一下ColorPoint的equals方法让它符合对称性,但会牺牲传递性;
@Override
public boolean equals(Object o) {
if(!(o instanceof Point)) return false;
if(!(o instanceof ColorPoint)) return o.equals(this);
return super.equals(o) && ((ColorPoint)o).color == color;
}
这方法解决了对称性问题但牺牲了传递性
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.equals(p2)); //返回true
System.out.println(p2.equals(p3)); //返回true
System.out.println(p1.equals(p3)); //返回false
因为前两个比较中忽略了颜色信息,第三个比较中比较了颜色,并且颜色不同,所以返回false;
注意:我们无法在扩展可实例化的类的同事,既增加新的值组件,同时又保留equals约定,除非愿意放弃面向对象的抽象所带来的的优势。但是我们可以在一个抽象类的子类中增加新的值组件,而不会违反equals约定。
防止违反一致性,无论类是否可变,都不要使equals方法依赖于不可靠的资源。
4、实现高质量equals的方法的诀窍:
a、使用==操作符检查“参数是否为这个对象的引用”。
b、使用instanceof操作符检查“参数是否为正确的类型”。
c、把参数转换为正确的类型。
d、对于该类中的每个“关键(significant)”域,检查参数中的域是否与该对象中对应的域相匹配。如果这些测试全部返回成功,则返回true,否则返回false。
注意:1、覆盖equals方法时总要覆盖hashcode方法;2、不要企图让equals方法过于智能;3、不要将equals声明中的Object对象替换为其他的类型。
1、覆盖equals的约定
自反性:对于任何非null的引用值x,x.equals(x)必须返回true。
对称性:对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。
传递性:对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)返回true,那么x.equals(z)也必须返回true。
一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致地返回true,或者一致的返回false。
对于任何非null的引用值x,x.equals(null)必须返回false。
2、违反对称性的案例:
public class CaseInsensitiveString {
private final String s;
public CaseInsensitiveString(String s){
if(s==null)
throw new NullPointerException();
this.s = s;
}
@Override
public boolean equals(Object o) {
if(o instanceof CaseInsensitiveString){
return s.equalsIgnoreCase(((CaseInsensitiveString) o).s);
}
if(o instanceof String)
return s.equalsIgnoreCase((String)o);
return false;
}
}
对于以上代码:如果假设现在有两个字符串,一个不区分大小写的,另一个是普通字符串
CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
String s = "polish";
cis.equals(s); //返回true
s.equals(cis); //返回false
虽然CaseInsensitiveString中的equals知道普通的字符串对象,但是String类中的equals方法并不知道不区分大小写的字符串,因此s.equals(cis)返回false。这就违反了对称性
3、违反对称性或传递性的案例:
public class Point {
private final int x;
private final int y;
public Point(int x,int y){
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if(!(o instanceof Point)) return false;
Point p = (Point)o;
return p.x == x && p.y == y;
}
}
现在扩展当前类,为点增加颜色属性
public class ColorPoint extends Point{
private final Color color;
public ColorPoint(int x,int y,Color color){
super(x,y);
this.color = color;
}
@Override
public boolean equals(Object o) {
if(!(o instanceof ColorPoint)) return false;
return super.equals(o) && ((ColorPoint)o).color == color;
}
}
这个问题在于,比较普通点和有色点,以及有色点和普通点反过来对比时可能会得到不同的结果。普通点和有色点比较时忽略了颜色信息,但有色点和普通点比较时永远返回false因为参数的类型不正确。这违反了对称性,改进一下ColorPoint的equals方法让它符合对称性,但会牺牲传递性;
@Override
public boolean equals(Object o) {
if(!(o instanceof Point)) return false;
if(!(o instanceof ColorPoint)) return o.equals(this);
return super.equals(o) && ((ColorPoint)o).color == color;
}
这方法解决了对称性问题但牺牲了传递性
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.equals(p2)); //返回true
System.out.println(p2.equals(p3)); //返回true
System.out.println(p1.equals(p3)); //返回false
因为前两个比较中忽略了颜色信息,第三个比较中比较了颜色,并且颜色不同,所以返回false;
注意:我们无法在扩展可实例化的类的同事,既增加新的值组件,同时又保留equals约定,除非愿意放弃面向对象的抽象所带来的的优势。但是我们可以在一个抽象类的子类中增加新的值组件,而不会违反equals约定。
防止违反一致性,无论类是否可变,都不要使equals方法依赖于不可靠的资源。
4、实现高质量equals的方法的诀窍:
a、使用==操作符检查“参数是否为这个对象的引用”。
b、使用instanceof操作符检查“参数是否为正确的类型”。
c、把参数转换为正确的类型。
d、对于该类中的每个“关键(significant)”域,检查参数中的域是否与该对象中对应的域相匹配。如果这些测试全部返回成功,则返回true,否则返回false。
注意:1、覆盖equals方法时总要覆盖hashcode方法;2、不要企图让equals方法过于智能;3、不要将equals声明中的Object对象替换为其他的类型。
相关文章推荐
- 多线程编程时的注意事项--摘抄自:Practical Java, Effective Java
- effective-java学习笔记(2)避免在程序中创建重复的对象
- Effective Java 的作者Joshua Bloch 跳槽到Google了
- Three tools that make Java code review painless and effective.
- 编写跨平台Java程序注意事项---摘抄
- java中的数组与集合的排序---摘抄自:http://blog.csdn.net/jonathan_q_bo/archive/2005/11/29/539043.aspx
- effective Java 学习笔记 (一)
- 关于在Java中 a!=a 值为真的解释(摘抄)
- 读Effective Java 备忘
- java中四种操作xml方式的比较(摘抄)
- Effective Java 学习笔记(一)
- effective java :17条interfaces 只應當被用來定義型別 (types)
- Effective Java 1
- Effective Java 3
- java 线程[摘抄至《Thinking in Java》]
- Effective Java 学习笔记(二)
- Effective Java 学习笔记(三)
- Effective Java中的57条建议
- 摘抄:JAVA编写规则 中级篇
- effective Java 学习笔记(三)