来自Effective Java的第9项(等于合同):示例是否正确?

adn*_*nan 5 java collections effective-java

Bloch的精彩书籍"Effective Java"指出,如果equals不对称,则Collections的行为contains是不确定的.

在他给出的例子中(通过下面的小修改再现),布洛赫说他看到了"假",但也可以看到真实或异常.

如果标准没有指定contains(Object o)检查e.equals(o)o.equals(e)集合中的每个项目以及前者是否已实现,则可以看到"true" .然而,Collections Javadoc明确指出它必须是后者(这是我观察到的).

所以我看到的唯一可能是"假"或可能是异常(但是字符串Javadoc似乎排除了后者).

我理解更广泛的观点,非对称性可能equals会导致集合之外的代码出现问题,但我不会在他引用的例子中看到它.

我错过了什么吗?

import java.util.List;
import java.util.ArrayList;

class CIString {
  private final String s;

  public CIString(String s) {
    this.s = s;
  }

  @Override public boolean equals( Object o ) {
    System.out.println("Calling CIString.equals from " + this.s );
    if ( o instanceof CIString) 
      return s.equalsIgnoreCase( ( (CIString) o).s);
    if ( o instanceof String) 
      return s.equalsIgnoreCase( (String) o );
    return false;
  }
  // Always override hashCode when you override equals
  // This is an awful hash function (everything collides -> performance is terrible!)
  // but it is semantically sound.  See Item 10 from Effective Java for more details.
  @Override public int hashCode() { return 42; }
}

public class CIS {
  public static void main(String[] args) {
   CIString a = new CIString("Polish");
   String s = "polish";

   List<CIString> list = new ArrayList<CIString>();
   list.add(a);
   System.out.println("list contains s:" + list.contains(s));
 }
}
Run Code Online (Sandbox Code Playgroud)

Tof*_*eer 3

现在是凌晨,所以也许我错过了你问题的真正要点,这段代码将失败:

\n\n
public class CIS \n{\n    public static void main(String[] args) \n    {\n        CIString a = new CIString("Polish");\n        String s = "polish";\n\n        List<String> list = new ArrayList<String>();\n        list.add(s);\n        System.out.println("list contains a:" + list.contains(a));\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

至少,你的代码找到它而我的代码没有找到它是很奇怪的(从理智的角度来看,这并不是你的代码显然是如何编写的:-)

\n\n

编辑:

\n\n
public class CIS {\n  public static void main(String[] args) {\n   CIString a = new CIString("Polish");\n   String s = "polish";\n\n   List<CIString> list = new ArrayList<CIString>();\n   list.add(a);\n   System.out.println("list contains s:" + list.contains(s));\n\n   List<String> list2 = new ArrayList<String>();\n   list2.add(s);\n   System.out.println("list contains a:" + list2.contains(a));\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在代码打印出来:

\n\n
list contains s:false\nCalling CIString.equals from Polish\nlist contains a:true\n
Run Code Online (Sandbox Code Playgroud)\n\n

这仍然没有意义……而且非常脆弱。如果两个对象像 a.equals(b) 一样相等,那么它们也必须像 b.equal(a) 一样相等,但您的代码并非如此。

\n\n

来自javadoc

\n\n
\n

它是对称的:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,\n x.equals(y) 才应返回 true。

\n
\n\n

所以,是的,书中的示例可能与集合 API 的 Javadoc 相矛盾,但原理是正确的。人们不应该创建一个行为奇怪的 equals 方法,否则最终会出现问题。

\n\n

编辑2:

\n\n

正文的要点是:

\n\n
\n

在 Sun\xe2\x80\x99s 当前的实现中,它恰好返回 false,但是 \n\xe2\x80\x99s 只是一个实现工件。在另一个实现中,它可以轻松地返回 true 或抛出运行时异常。一旦你违反了 equals 契约,你就根本不知道其他对象在面对你的对象时会如何表现。

\n
\n\n

然而,考虑到 Javadoc 所说的内容,该行为似乎是固定的,而不是实现工件。

\n\n

如果它不在 javadoc 中,或者 javadoc 不属于规范的一部分,那么它可能会在以后发生更改,并且代码将不再工作。

\n