任何两个对象对于 “它们是否相等” 问题必须保持一致。与第一个要求不同,若无意中违反这一条,这种情形倒是不难想象。例如,下面的类,它实现了一个不区分大小写的字符串。字符串由 toString
保存,但在 equals
操作中被忽略。
/** * 覆写 Object 的 equals 方法时, 必须满足对称性要求 (Symmetry) * 即 x.equals(y) 与 y.equals(x) 的行为必须一致 * <p> * 下面展示了一个违反该约定的情况 * 一旦违反了 equals 约定, 当其他对象面对你的对象时, 你完全不知道这些对象的行为会怎样. */ public class SymmetryBadDemo { public static final class CaseInsensitiveString { private final String s; public CaseInsensitiveString(String s) { this.s = s; } /** * 这里的 equals 方法存在一个漏洞 * 就是一个 CaseInsensitiveString 对象和一个 String 对象比较的时候会出现问题 * 造成这一现象的原因主要是 */ @Override public boolean equals(Object o) { if (o instanceof CaseInsensitiveString) { return s.equalsIgnoreCase(((CaseInsensitiveString) o).s); } if (o instanceof String) { return s.equalsIgnoreCase(((String) o)); } return false; } @Override public int hashCode() { return Objects.hash(s.toLowerCase()); } } public static void main(String[] args) { CaseInsensitiveString cis = new CaseInsensitiveString("Polish"); String s = "polish"; System.out.println("违反自反性的例子"); System.out.println("cis.equals(s) = " + cis.equals(s)); System.out.println("s.equals(cis) = " + s.equals(cis)); } }
为了解决这个问题,是需要禁止 String
与 CaseInsensitiveString
之间的互操作即可。
public class SymmetryGoodDemo { public static final class CaseInsensitiveString { private final String s; public CaseInsensitiveString(String s) { this.s = s; } /** * 解决方法: 禁止与 String 类型的对象互操作 * 由于 CaseInsensitiveString 与 String 之间没有任何继承关系. * 而 equals 函数输入的是一个 Object 对象 * 要避免 CaseInsensitiveString 与 String 之间进行比较 * 就要在 equals 中主动判断 Object 对象的实际类型与当前类的类型是否一致 */ @Override public boolean equals(Object o) { return o instanceof CaseInsensitiveString && ((CaseInsensitiveString) o).s.equalsIgnoreCase(s); } @Override public int hashCode() { return Objects.hash(s.toLowerCase()); } } public static void main(String[] args) { CaseInsensitiveString cis = new CaseInsensitiveString("Polish"); String s = "polish"; System.out.println("遵守自反性的修改"); System.out.println("cis.equals(s) = " + cis.equals(s)); System.out.println("s.equals(cis) = " + s.equals(cis)); } }