如果类具有自己特定的“逻辑相等”概念(不同于对象等同概念),而且超类还没有覆盖equals以实现期望的行为,这时我们就需要覆盖equals方法,这通常属于“值类”的情形,例如Integer或者是Data,程序员在利用equals方法来比较值对象的引用时,希望知道它们在逻辑上是否相等,而不是想了解它们是否指向同一个对象。
在覆盖equals方法时,你必须要遵守它的通用约定。下面是约定的内容,来自Object的规范:
l自反性:对于任何非null的引用值x,x.equals(x)必须返回true。如果自已不等于自己的话,将其放入集合中后,该集合的contains方法将告诉你,集合中不包括你刚添加的实例。
l对称性:对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。这就要求不同类的实例如果在逻辑值相同的情况下,要求这两个实例所对应的类的equals方法比较逻辑要相同,不然的话,对称性将不再满足。
l传递性:对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true。
l一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致地返回true,或者一致地返回false。
l非空性:对于任何非null的引用值x,x.equals(null)必须返回false。
实现高质量的equals方法:
1、使用==操作符检查“参数是否为这个对象的引用”。如果是,则返回true,这只不过是一种性能优化,如果比较操作有可能很昂贵,就值得这么做。
2、使用instanceof操作符检查“参数是否为正确的类型”。如果不是,则返回false。一般说来所谓“正确的类型”是指定equals所在的那个类。有些情况下,是指该类所实现的某个接口。如果类实现的接口改进了equals约定,允许在实现了该接口的类之间进行比较,那么就使用接口。集合接口如Set、List、Map和Map.Entry具有这样的特性。注,这步会过滤掉null,因为null instanceof XX一定会返回false。另外,要注意的是,如果你只与自己本身类型的类相比,则可以使用if(getClass() == obj.getClass())来限制为同一个类比较而不希望是父类或其子类(思想来源于《PRactice Java》)。
3、把参数转换成正确的类型。因为转换之前进行过instanceof测试,所以确保会成功。
4、对于该类中的每个“关键”域,检查参数中的域是否与该对象中对应的域相匹配。如果这些测试全部成功,则返回true,否则返回false。
对于既不是float也不是double类型的基本类型域,可以使用==操作符进行比较;对于对象引用域,可以递归地调用equals方法;对于float域,可以使用Float.compare方法;对于double域,则使用Double.compare。对于数组域,则要把以上这些指导原则应用到每个元素上,如果数组域中的每个元素都需要比较的话,可以使用1.5版本中发行的Arrays.equals方法。
对于float和double域进行特殊的处理是有必要的,因为存在着Float.NaN、-0.0f以及类似的double常量,详细信息请参考Float.equals的文档,看看Float.equals源码与文档描述:
publicbooleanequals(Object obj) {
return(objinstanceofFloat)
&& (floatToIntBits(((Float) obj).value) == floatToIntBits(value));
}
在比较是否相等时使用是floatToIntBits(float)方法,即将浮点的二进制位看作是整型位后比较的。注意,在大多数情况下,对于Float类的两个实例f1和f2,让f1.equals(f2)的值为true的条件是当且仅当f1.floatValue() == f2.floatValue()的值也为true。但是也有下列两种例外:
l如果f1和f2都表示Float.NaN(规定Float.NaN = 0x7fc00000),那么即使Float.NaN = = Float.NaN的值为false,equals方法也将返回true(因为他们所对应的整型位是相同的)。
l如果f1表示+0.0f,而f2表示-0.0f,或相反的情况,则equal测试返回的值是false(因为他们所对应的整型位是不同的),即使0.0f = = -0.0f的值为true也是如此。
另外,来看看Float.compare的源码:
publicstaticintcompare(floatf1,floatf2) {
if(f1 < f2)
return-1; // Neithervalis NaN, thisVal is smaller
if(f1 > f2)
return1; // Neithervalis NaN, thisVal is larger
intthisBits = Float.floatToIntBits(f1);
intanotherBits = Float.floatToIntBits(f2);
return(thisBits == anotherBits ? 0 : // Values are equal
(thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
1)); // (0.0, -0.0) or (NaN, !NaN)
}
compare是从数字上比较两个Float对象。在应用到基本float值时,有两种方法来比较执行此方法产生的值与执行Java语言的数字比较运算符(<、<=、==和>= >)产生的那些值之间的区别:
l该方法认为Float.NaN将等于其自身,且大于其他所有float值(包括Float.POSITIVE_INFINITY)。
l该方法认为0.0f将大于-0.0f。
请记住,如果是通过Java语言的数字比较运算符(<、<=、==和>= >)而不是compare方法来比较时,只要其中有一个操作为Float.NaN,那么比较结果就是false。
对象引用域的值为null是有可能的,所以,为了避免可能导致的空指针异常,则使用下面的作法:
新闻热点
疑难解答