码迷,mamicode.com
首页 > 编程语言 > 详细

effective java读书笔记——对于所有对象都通用的方法

时间:2015-04-02 22:16:58      阅读:157      评论:0      收藏:0      [点我收藏+]

标签:

Java中的所有类都继承自Object类,Object类中有许多通用的方法,这一章要讨论的是:对于Object类中的通用方法,我们的类要不要继承,以及继承时需要注意的事项。

第1条:equals(),覆盖时请遵守通用约定

首先看一下不需要覆盖的情况:

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!