第三章 对于所有对象都通用的方法
第8条 覆盖equals时请遵守通用约定
一般来说,没有逻辑相等的概念的时候都不要覆盖equals方法。或者说超类已经提供的符合子类equals的方法。当然,类时私有的也不应该覆盖。
覆盖equals方法的情况一般会是“值类(value class)”。覆盖equals方法时应该遵守它的通用约定:
• Reflexive: For any non-null reference value x, x.equals(x) must return true.
• Symmetric: For any non-null reference values x and y, x.equals(y) must return
true if and only if y.equals(x) returns true.
• Transitive: For any non-null reference values x, y, z, if x.equals(y) returns
true and y.equals(z) returns true, then x.equals(z) must return true.
• Consistent: For any non-null reference values x and y, multiple invocations
of x.equals(y) consistently return true or consistently return false, provided
no information used in equals comparisons on the objects is modified.
• For any non-null reference value x, x.equals(null) must return false.
翻译过来依次是自反性,对称性,传递性,一致性和对于任何非null引用值x,x.equals(null)一定返回false。
对于自反性来说,因为很多其他类(比如集合类)通过equals判断是否持有这个对象,如果违反这一条的话,那么调用对应的是否存在这个对象的方法时将返回false。当然违背这条的反例还真不好找。
对于对称性来说,反例就比较好举出了(书中代码):
// Broken - violates symmetry!
public final class CaseInsensitiveString {
private final String s;
public CaseInsensitiveString(String s) {
if (s == null)
throw new NullPointerException();
this.s = s;
}
// Broken - violates symmetry!
@Override public boolean equals(Object o) {
if (o instanceof CaseInsensitiveString)
return s.equalsIgnoreCase(
((CaseInsensitiveString) o).s);
if (o instanceof String) // One-way interoperability!
return s.equalsIgnoreCase((String) o);
return false;
}
... // Remainder omitted
}
传递性引用书中一句话: There is no way to extend an
instantiable class and add a value component while preserving the equals
contract, unless you are willing to forgo the benefits of object-oriented abstraction.
但是还是有一个方法解决子类附加组件而带来的传递性问题,就是用复合而非继承(附书中代码):
// Adds a value component without violating the equals contract
public class ColorPoint {
private final Point point;
private final Color color;
public ColorPoint(int x, int y, Color color) {
if (color == null)
throw new NullPointerException();
point = new Point(x, y);
this.color = color;
}
/**
* Returns the point-view of this color point.
*/
public Point asPoint() {
return point;
}
@Override public boolean equals(Object o) {
if (!(o instanceof ColorPoint))
return false;
ColorPoint cp = (ColorPoint) o;
return cp.point.equals(point) && cp.color.equals(color);
}
... // Remainder omitted
}
比较Point时可以使用asPoint方法返回Point视图。
一致性呢,一句话概括下,无论类是否可变,都不要使equals方法依赖于不可靠的资源。
书中总结出了实现高质量equals方法的诀窍:
1. Use the == operator to check if the argument is a reference to this object.
2. Use the instanceof operator to check if the argument has the correct type.
3. Cast the argument to the correct type.
4. For each “significant” field in the class, check if that field of the argument
matches the corresponding field of this object.
For primitive fields whose type is not float or double, use the == operator for
comparisons; for object reference fields, invoke the equals method recursively;
for float fields, use the Float.compare method; and for double fields, use
Double.compare. The special treatment of float and double fields is made
necessary by the existence of Float.NaN, -0.0f and the analogous double
constants; see the Float.equals documentation for details. For array fields,
apply these guidelines to each element. If every element in an array field is significant,
you can use one of the Arrays.equals methods added in release 1.5.
对于null合法的域可以采用如下代码:
(field == null ? o.field == null : field.equals(o.field))
5. When you are finished writing your equals method, ask yourself three
questions: Is it symmetric? Is it transitive? Is it consistent?
还有一些告诫:
• Always override hashCode when you override equals (Item 9).
• Don’t try to be too clever.
• Don’t substitute another type for Object in the equals declaration
第9条 覆盖equals时总要覆盖hashcode
这一条主要是覆盖hashcode的时候一般采用如下的方法:
1. Store some constant nonzero value, say, 17, in an int variable called result.
2. For each significant field f in your object (each field taken into account by the
equals method, that is), do the following:
a. Compute an int hash code c for the field:
i. If the field is a boolean, compute (f ? 1 : 0).
ii. If the field is a byte, char, short, or int, compute (int) f.
iii. If the field is a long, compute (int) (f ^ (f >>> 32)).
iv. If the field is a float, compute Float.floatToIntBits(f).
v. If the field is a double, compute Double.doubleToLongBits(f), and
then hash the resulting long as in step 2.a.iii.
vi. If the field is an object reference and this class’s equals method
compares the field by recursively invoking equals, recursively
invoke hashCode on the field. If a more complex comparison is
required, compute a “canonical representation” for this field and
invoke hashCode on the canonical representation. If the value of the
field is null, return 0 (or some other constant, but 0 is traditional).
vii. If the field is an array, treat it as if each element were a separate field.
That is, compute a hash code for each significant element by applying
these rules recursively, and combine these values per step 2.b. If every
element in an array field is significant, you can use one of the
Arrays.hashCode methods added in release 1.5.
b. Combine the hash code c computed in step 2.a into result as follows:
result = 31 * result + c;
3. Return result.
4. When you are finished writing the hashCode method, ask yourself whether
equal instances have equal hash codes. Write unit tests to verify your intuition!
If equal instances have unequal hash codes, figure out why and fix the problem.
还有一个印象比较深刻的是: Do not be tempted to exclude significant parts of an object from the hashcode computation to improve performance.因为这可能导致hashmap之类的性能很差。
第10条 始终覆盖toString
toString可以很好的表述一个类,在打log等等方面都非常有用。
第11条 谨慎覆盖clone
最好呢就是别去覆盖这个方法,需要复制的话可以使用拷贝构造器和静态拷贝工厂。
第12条 考虑实现Comparable接口
有一句话印象蛮深刻的: By implementing Comparable, you allow your class to interoperate with all of the many generic algorithms and collection implementations that depend on this
interface. You gain a tremendous amount of power for a small amount of effort.
实现compareTo方法时从最关键的域开始比较,逐步进行到所有的重要域。
这里顺便提一下书中的一个对compareTo的一个建议:
It is strongly recommended, but not strictly required, that (x.compareTo(y)
== 0) == (x.equals(y)). Generally speaking, any class that implements the
Comparable interface and violates this condition should clearly indicate this
fact. The recommended language is “Note: This class has a natural ordering
that is inconsistent with equals.”
如果违反了这个建议书中提到:
The final paragraph of the compareTo contract, which is a strong suggestion
rather than a true provision, simply states that the equality test imposed by the
compareTo method should generally return the same results as the equals
method. If this provision is obeyed, the ordering imposed by the compareTo
method is said to be consistent with equals. If it’s violated, the ordering is said to
be inconsistent with equals. A class whose compareTo method imposes an order
that is inconsistent with equals will still work, but sorted collections containing
elements of the class may not obey the general contract of the appropriate collection interfaces (Collection, Set, or Map). This is because the general contracts
for these interfaces are defined in terms of the equals method, but sorted collections
use the equality test imposed by compareTo in place of equals. It is not a
catastrophe if this happens, but it’s something to be aware of.
分享到:
相关推荐
"Effective Java读书笔记" Effective Java是一本关于Java编程语言的经典书籍,本笔记主要总结了Java语言的发展历程、静态工厂方法的应用、构造器模式的使用等重要知识点。 一、Java语言的发展历程 Java语言的发展...
《技术人的管理之路》读书笔记 --思维导图 《技术人的管理之路》读书笔记 --思维导图 《技术人的管理之路》读书笔记 --思维导图 《技术人的管理之路》读书笔记 --思维导图 《技术人的管理之路》读书笔记 --思维导图 ...
《Effective Java》是Java开发领域的经典著作,作者Joshua Bloch深入浅出地阐述了编写高效、健壮的Java代码的技巧和最佳实践。以下是对该书部分内容的详细解释: 1. **产生和销毁对象** - Item1:静态工厂方法相比...
学生读书笔记共享-学生读书笔记共享系统-学生读书笔记共享系统源码-学生读书笔记共享管理系统-学生读书笔记共享管理系统java代码-学生读书笔记共享系统设计与实现-基于springboot的学生读书笔记共享系统-基于Web的...
《java并发编程实战》读书笔记-第3章-对象的共享,脑图形式,使用xmind8制作 包括可见性、发布与逸出、线程封闭、不可变性、安全发布等内容
学生读书笔记共享-学生读书笔记共享系统-学生读书笔记共享系统源码-学生读书笔记共享管理系统-学生读书笔记共享管理系统java代码-学生读书笔记共享系统设计与实现-基于springboot的学生读书笔记共享系统-基于Web的...
《java并发编程实战》读书笔记-第3章-对象的共享,脑图形式,使用xmind8制作 包括线程安全类设计、实例封闭、线程安全性委托、现有线程安全类中添加功能和文档化同步策略等内容
《java并发编程实战》读书笔记-第3章-对象的共享,脑图形式,使用xmind8制作 包括同步容器类、并发容器类、阻塞队列和生产者消费者模式、阻塞和中断方法、同步工具类。最后是构建高效且可伸缩的结果缓存
【标题】"java读书笔记笔记笔记笔记笔记笔记" 暗示了这是一份关于Java编程语言的学习笔记,可能包含了作者在阅读Java相关书籍时所做的重要记录和理解。笔记通常涵盖了语言的基础概念、核心特性、类与对象、内存管理...
《Effective Java》是Java...以上仅是《Effective Java》一书中部分核心知识点的概述,实际的读书笔记中会更详细地解释这些概念,并给出具体的示例代码。通过深入学习和实践,开发者可以极大地提升其Java编程的水平。
《学会提问》读书笔记-秋叶、《学会提问》读书笔记-秋叶、《学会提问》读书笔记-秋叶、《学会提问》读书笔记-秋叶、《学会提问》读书笔记-秋叶、《学会提问》读书笔记-秋叶
尚硅谷康师傅java学习笔记。 、2020-4-5 java学习笔记 2020-4-6 java笔记 ---内部类 2020-4-6 java笔记 ---异常 2020-4-6 java笔记 --多线程 2020-4-8 java笔记 String类 2020-4-9 java 比较器 2020-4-10 java笔记 ...
计算机网络设计第一章读书笔记------.pdf该文档详细且完整,值得借鉴下载使用,欢迎下载使用,有问题可以第一时间联系作者~
Java基础每日复习笔记-JavaSE高级阶段.2020-10-13-211312.edf
《java并发编程实战》读书笔记-第2章-线程安全性,脑图形式,使用xmind8制作 包括引言、线程安全性定义、原子性、加锁机制、使用锁保护状态、活跃性与性能等内容