第二章 所有对象共有的方法
尽管Object是一个具体的类,但是它设计的主要目标是为了去扩展,所以他的所有的非final的方法
(equals, hashCode, toString, clone, and finalize)都有一个明确一般的约定。
条款8 覆写equals要遵循一般的约定:
不覆写equals的情况:
1、每一个实例本身就是唯一的(如Thread)
2、对是否提供逻辑的equals不关心(如Random没有必要取测试两个Random实例生成的随即序列是否一致
)。
3、超类已经覆写了equals,并且这个Equals对子类也适应。
4、如果类是私有的或者包私有的,那么你要确定equals将不会被调用。
如何恰当的实现equals:
1、自反省:这个不可能被违反
2、对称性:去Wapper一个类,然后增加点属性,如果试图兼容对原始类型的equals,就会违反
因为原始类型不可能去预测和兼容对wrapper之后类的equals。
3、传递性:子类试图兼容对父类的equals
[code]
// Broken - violates transitivity!
@Override public boolean equals(Object o) {
if (!(o instanceof Point))
return false;
// If o is a normal Point, do a color-blind comparison
if (!(o instanceof ColorPoint))
return o.equals(this);
// o is a ColorPoint; do a full comparison
return super.equals(o) && ((ColorPoint)o).color == color;
}
[/code]
ColorPoint p1 = new ColorPoint(1, 2, Color.RED);
Point p2 = new Point(1, 2);
ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);
p1.equals(p2) p2.equals(p3) 但是p1.equals(p3) return false;
这样实际没有办法给一个类的子类增加一个属性,而保留equals的约定。
文中说试图使用getClass来消除继承的影响,而保证只有每个类自己的实例
之间的比较会相等,这会违反面向对象的重要法则Liskov替换法则,
书中举的这个例子感觉还是很牵强的,父类对象继承下来的equals,如果子类
没有实例字段的增加,就应该默认支持(new CounterPoint(1,0)).equals(
new new Point( 1, 0))么?我感觉未必如此。
其实使用instanceof代替getClass的在equals使用的唯一理由是:
使用getClass就无法调用super.equals来判断继承下来的字段equals了,而只能把继承下来的
字段一一比较,这点对编写代码,特别是使用第三方的代码而来说是一个很繁琐而困难的事情。
而使用getClass保证只有每个类自己的实例之间的比较才可能相等,这其实是一个好的
事情,因为我们几乎不会去判断一个子类和父类的equals关系,事实上如书所说,
无法保证equals的Contract会使得代码行为不确定而诡异。
4、一致性(例如URL使用ip来判断equals,会因为网络问题而产生不一致的问题)
不要让equals依赖与不可靠的资源
5、非null性,事实上instanceof保证了这一点。
总结:
1、首先使用==做判断能够提高比较的性能
2、使用instanceof来判断参数是否为正确的类型
3、强制转换成正确的类型。
4、对每个字段判断是否相等。
5、验证equals的对称性传递性和一致性
6、不要过度的使用equals(File)
7、使用@Override来确保equals函数签名的正确。
8、equals的对像hashcode要相同。
条款9 覆写equals一定要覆写hashcode
Object specification指出了equals的object具有相同的hashCode。
因为hashCode在HashMap、HashSet等集合中使用,它会根据hashCode来retrieve对象。
如何设计hashCode:
对每一个字段转化成整数:result = 31 * result + c;
对于不变对象考虑hashCode缓存:
条款10 一直要覆写toString方法:
提供好的toString方法,会让你的代码使用起来有一种愉快的感觉。print,printf用起来会很爽。
toString方法要返回对象中所有有用的信息。
是否在文档中指定toString返回的格式,都要在文档中表明意图。
指定格式优点更清晰,缺点用户如果试图依赖于toString的返回的格式,会使得更改toString的实现困
难。让用户依赖于更少的实现,使得API的实现重新实现更容易。
条款11 明智的覆写clone方法。
Cloneable接口作为一个mixin接口,来说明一个对象允许复制,但是由于clone作为Object的一个protected方法,它可能无法去体现这一点,这是很不幸的。但可能考虑Clone方法存在一定的安全问题
才这么做的。
当成员中有Object reference的时候考虑是否要deep copy.
特别是写一个类,意图被别的类扩展的话,一定要妥善的实现Clone方法,否则别的类也无法正常clone。
条款 12 考虑实现Comparable接口
对于想按照一种自然的方式来排序对象的话,实现Comparable接口是一个明智的选择。
当然像Collections的sort提供了回调的Comparator参数,这一点对于没有实现Comparable接口或者
想在某个时候定制这种比较方法非常有效。
尽管Object是一个具体的类,但是它设计的主要目标是为了去扩展,所以他的所有的非final的方法
(equals, hashCode, toString, clone, and finalize)都有一个明确一般的约定。
条款8 覆写equals要遵循一般的约定:
不覆写equals的情况:
1、每一个实例本身就是唯一的(如Thread)
2、对是否提供逻辑的equals不关心(如Random没有必要取测试两个Random实例生成的随即序列是否一致
)。
3、超类已经覆写了equals,并且这个Equals对子类也适应。
4、如果类是私有的或者包私有的,那么你要确定equals将不会被调用。
如何恰当的实现equals:
1、自反省:这个不可能被违反
2、对称性:去Wapper一个类,然后增加点属性,如果试图兼容对原始类型的equals,就会违反
因为原始类型不可能去预测和兼容对wrapper之后类的equals。
3、传递性:子类试图兼容对父类的equals
[code]
// Broken - violates transitivity!
@Override public boolean equals(Object o) {
if (!(o instanceof Point))
return false;
// If o is a normal Point, do a color-blind comparison
if (!(o instanceof ColorPoint))
return o.equals(this);
// o is a ColorPoint; do a full comparison
return super.equals(o) && ((ColorPoint)o).color == color;
}
[/code]
ColorPoint p1 = new ColorPoint(1, 2, Color.RED);
Point p2 = new Point(1, 2);
ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);
p1.equals(p2) p2.equals(p3) 但是p1.equals(p3) return false;
这样实际没有办法给一个类的子类增加一个属性,而保留equals的约定。
文中说试图使用getClass来消除继承的影响,而保证只有每个类自己的实例
之间的比较会相等,这会违反面向对象的重要法则Liskov替换法则,
书中举的这个例子感觉还是很牵强的,父类对象继承下来的equals,如果子类
没有实例字段的增加,就应该默认支持(new CounterPoint(1,0)).equals(
new new Point( 1, 0))么?我感觉未必如此。
其实使用instanceof代替getClass的在equals使用的唯一理由是:
使用getClass就无法调用super.equals来判断继承下来的字段equals了,而只能把继承下来的
字段一一比较,这点对编写代码,特别是使用第三方的代码而来说是一个很繁琐而困难的事情。
而使用getClass保证只有每个类自己的实例之间的比较才可能相等,这其实是一个好的
事情,因为我们几乎不会去判断一个子类和父类的equals关系,事实上如书所说,
无法保证equals的Contract会使得代码行为不确定而诡异。
4、一致性(例如URL使用ip来判断equals,会因为网络问题而产生不一致的问题)
不要让equals依赖与不可靠的资源
5、非null性,事实上instanceof保证了这一点。
总结:
1、首先使用==做判断能够提高比较的性能
2、使用instanceof来判断参数是否为正确的类型
3、强制转换成正确的类型。
4、对每个字段判断是否相等。
5、验证equals的对称性传递性和一致性
6、不要过度的使用equals(File)
7、使用@Override来确保equals函数签名的正确。
8、equals的对像hashcode要相同。
条款9 覆写equals一定要覆写hashcode
Object specification指出了equals的object具有相同的hashCode。
因为hashCode在HashMap、HashSet等集合中使用,它会根据hashCode来retrieve对象。
如何设计hashCode:
对每一个字段转化成整数:result = 31 * result + c;
对于不变对象考虑hashCode缓存:
int result = hashCode;
if (result == 0) {//if not cacalute.
//...
}
return result;
条款10 一直要覆写toString方法:
提供好的toString方法,会让你的代码使用起来有一种愉快的感觉。print,printf用起来会很爽。
toString方法要返回对象中所有有用的信息。
是否在文档中指定toString返回的格式,都要在文档中表明意图。
指定格式优点更清晰,缺点用户如果试图依赖于toString的返回的格式,会使得更改toString的实现困
难。让用户依赖于更少的实现,使得API的实现重新实现更容易。
条款11 明智的覆写clone方法。
Cloneable接口作为一个mixin接口,来说明一个对象允许复制,但是由于clone作为Object的一个protected方法,它可能无法去体现这一点,这是很不幸的。但可能考虑Clone方法存在一定的安全问题
才这么做的。
当成员中有Object reference的时候考虑是否要deep copy.
特别是写一个类,意图被别的类扩展的话,一定要妥善的实现Clone方法,否则别的类也无法正常clone。
条款 12 考虑实现Comparable接口
对于想按照一种自然的方式来排序对象的话,实现Comparable接口是一个明智的选择。
当然像Collections的sort提供了回调的Comparator参数,这一点对于没有实现Comparable接口或者
想在某个时候定制这种比较方法非常有效。