Java7 引入了一些新的方法,可以更方便地处理 equals() 和 hashCode() 方法中的 null 值,以及 compareTo() 方法中的数字比较。
假设你需要为下面的实体类 Person 实现 equals 方法:
public class Person { private String first; private String last; //.... }
首先,你不得不进行参数非 null 判断、参数类型判断,最后将参数转换为一个 Person 对象:
@Override public boolean equals(Object o) { // 判断两者是否指向同一个对象 if (this == o) { return true; } // 参数非空判断,类型判断 if (o == null || getClass() != o.getClass()) { return false; } // 参数转换为 Person 类型 Person person = (Person) o; if (!first.equals(person.first)) { return false; } return last.equals(person.last); }
不幸的是,在 Java7 之前,每个类的 equals() 方法中都要这么做一遍(做了很多重复的工作)。在 Java7 之后,你不用再担心 Person 对象的 first 或者 last 字段可能为空,而只需调用:
@Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Person person = (Person) o; return Objects.equals(first, person.getFirst()) && Objects.equals(last, person.getLast()); }
对于 Objects.equals(a,b),如果 a 和 b 都是 null,则返回 true;如果只有 a 为 null,则返回 false;其他情况则返回 a.equals(b)。
注意:一般来说,你应该把以前调用 a.equals(b) 的地方改为调用 Objects.equals(a,b)。
我们还以上面的 Person 类为例,来计算它的哈希码。下面时通过 IDEA 自动帮我们生成的:
@Override public int hashCode() { int result = first.hashCode(); result = 31 * result + last.hashCode(); return result; }
如果 first 和 last 为 null,上面调用将会抛出 NullPointerException 异常。此时,我们可以借助 JDK7 提供的 Objects 类的 hashCode() 静态方法。对于 null 对象,Objects.hashCode() 方法会返回 0,因此你可以像下面这样来实现 hashCode() 方法:
@Override public int hashCode() { return 31 * Objects.hashCode(first) + Objects.hashCode(last); }
但是,我们还有更好的选择。Java7 中引入的可变参数方法 Objects.hash() 允许你指定任意个对象,并且它们的哈希码会被自动组合起来:
@Override public int hashCode() { return Objects.hash(first, last); }
⚠️注意:
(1)Objects.hash() 只是会调用从 Java5 开始就存在的 Arrays.hash() 方法。但是 Arrays.hash() 方法不是一个支持可变参数的方法,因此在使用上也不是很方便。
(2)对于 toString() 方法,你应该总是调用 String.valueOf(obj),因为它可以安全地处理 null 对象。如果 obj 为 null,那么会返回字符串 “null”。如果不喜欢这种方式,也可以调用 Objects.toString() 方法,并提供一个用于 null 对象的值。例如,Objects.toString(obj, "")。
当你通过比较器来比较整型值时,因为允许返回任意负值或正值,所以它会试图返回二者之间相差的大小,但是实际上只需要知道符号就足够了。例如,假设你实现了一个 Point 类:
public int compareTo(Point other) { int diff = x - other.x; // 有溢出的风险 if(diff != 0) return diff; return y - other.y; }
但是这是有问题的。如果 x 非常大并且 other.x 是一个负数,那么二者之间的差值可能会导致溢出,如:Integer.MAX_VALUE - (-100) 结果为 -2147483549。这也使得实现 compareTo 方法变得很复杂。
在 Java7 后,你可以使用静态方法 Integer.compare() 来实现:
public int compareTo(Point other)( int diff = Integer.compare(x, other.x); // 没有溢出的风险 if(diff != 0) return diff; return Integer.compare(y,other.y); }
过去,某些开发人员会使用 new Integer(x).compareTo(other.x) 的方式,但是这会创建两个会自动装箱/拆箱的整型对象。相比之下,静态方法 compare() 使用的是 int 参数。
此外,Long、Short、Byte 和 Boolean 也都提供了各自的静态方法 compare()。如果你需要比较两个字符型值(char),可以直接将它们相减,因为这不会导致结果溢出(当然,这对于 short 和 byte 类型值也是一样)。
⚠️注意:Double 和 Float 类中的静态方法 compare() 从 Java 1.2 开始就存在了。