有效Java中的第9项(等于合同):示例正确吗?
布洛赫的精彩著作《有效Java》指出,如果有效Java中的第9项(等于合同):示例正确吗?,java,collections,effective-java,Java,Collections,Effective Java,布洛赫的精彩著作《有效Java》指出,如果等于是不对称的,那么包含的集合的行为是不确定的 在他给出的例子中(在下面稍加修改后复制),布洛赫说他看到了“假”,但也可能看到了真或例外 如果标准未指定集合中每个项目的是否包含(对象o)检查e.equals(o)或o.equals(e),并且前者已实现,则可以看到“true”。然而,报告明确指出,必须是后者(这是我观察到的) 因此,我看到的唯一可能性是“错误”或可能是例外(但似乎排除了后者) 我理解更广泛的观点,不对称的equalsis很可能会导致集合之
等于
是不对称的,那么包含的集合的行为是不确定的
在他给出的例子中(在下面稍加修改后复制),布洛赫说他看到了“假”,但也可能看到了真或例外
如果标准未指定集合中每个项目的是否包含(对象o)
检查e.equals(o)
或o.equals(e)
,并且前者已实现,则可以看到“true”。然而,报告明确指出,必须是后者(这是我观察到的)
因此,我看到的唯一可能性是“错误”或可能是例外(但似乎排除了后者)
我理解更广泛的观点,不对称的equals
is很可能会导致集合之外的代码出现问题,但我没有看到他引用的例子
我错过什么了吗
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));
}
}
import java.util.List;
导入java.util.ArrayList;
类顺串{
私有最终字符串s;
公共CI字符串(字符串s){
这个.s=s;
}
@重写公共布尔等于(对象o){
System.out.println(“从“+this.s”调用CIString.equals);
if(i字符串的o实例)
返回s.equalsIgnoreCase(((CIString)o.s);
如果(字符串的o实例)
返回s.equalsIgnoreCase((字符串)o);
返回false;
}
//重写等于时始终重写哈希代码
//这是一个糟糕的哈希函数(一切都会发生冲突->性能糟糕!)
//但它在语义上是合理的,更多细节请参见有效Java中的第10项。
@重写公共int hashCode(){return 42;}
}
公共类CI{
公共静态void main(字符串[]args){
CIString a=新CIString(“波兰”);
字符串s=“波兰”;
列表=新的ArrayList();
列表.添加(a);
System.out.println(“列表包含s:+list.contains(s));
}
}
现在是凌晨,所以也许我没有理解你问题的真正意思,这段代码将失败:
public class CIS
{
public static void main(String[] args)
{
CIString a = new CIString("Polish");
String s = "polish";
List<String> list = new ArrayList<String>();
list.add(s);
System.out.println("list contains a:" + list.contains(a));
}
}
这仍然没有意义。。。而且非常脆弱。如果两个对象像a.equals(b)一样相等,那么它们也必须像b.equal(a)一样相等,这不是代码的情况
从:
它是对称的:对于任何非空参考值x和y,
x、 当且仅当y.equals(x)返回时,equals(y)应返回true
对
因此,是的,本书中的示例可能与collections API的Javadoc相反,但原理是正确的。我们不应该创建一个行为异常的equals方法,否则最终会出现问题
编辑2:
正文的要点是:
在Sun当前的实现中,它恰好返回false,但是
这只是一个实现工件。在另一个实现中,它
可以很容易地返回true或抛出运行时异常。一旦
你违反了平等合同,你根本不知道其他人
当面对对象时,对象将表现出行为
然而,考虑到Javadoc所说的,行为似乎是固定的,而不是实现工件
如果它不在javadoc中,或者javadoc不打算成为规范的一部分,那么它可能会在以后更改,代码将不再工作。在我现在看的书(第二版)的副本中,项目编号是8,而关于对称性的整个章节都非常糟糕
您提到的特定问题似乎是由于使用代码过于接近实现而导致的,模糊了作者试图表达的观点。我的意思是,我看了一下列表.contains(s)
,我看到了ArrayList和String,所有关于返回true或抛出异常的推理对我来说毫无意义,真的
- 我不得不将“使用代码”从实现中进一步移开,以了解它可能是怎样的:
void test(List<CIString> list, Object s) {
if (list != null && list.size() > 0) {
if (list.get(0).equals(s)) { // unsymmetric equality in CIString
assert !list.contains(s); // "usage code": list.contain(s)
}
}
}
如果我们将上面的列表和字符串“polish”传递给测试,会发生什么list.get(0).equals(s)
仍将通过检查,但list.contains(s)
将从中抛出ClassCastException
这似乎就像布洛赫在提到list.contains
可能会再次抛出异常时想到的情况一样,尽管首先通过了equals
检查。细微的挑剔:您的示例没有实现或暗示实现hashcode()方法,这意味着CIString没有实现契约,一些相等的对象将具有不相等的哈希码。我知道这不是你的问题,但为了以后在谷歌上搜索你的问题的读者,你能至少写一个关于这个问题的注释吗?我添加了一个哈希函数,并对它的必要性进行了注释。请参阅我的第二次编辑以获得澄清,你所指的方式是JDK 1.4-下面使用泛型的代码看起来有点奇怪。无论如何,javadoc文本仍然与ion JDK 1.5/1.6相同,所以这是一个次要的点,当您运行示例时,他的“调用CIString.equals…
”消息是否出现在输出中?只是澄清一下,我在Bloch得到了相同的输出,列表包含s:false
-如果将println消息写成“list.contains(a)返回”+list.contains(a)
你的观点是正确的,但是我的问题的核心是布洛赫的主张是否成立,也就是说,你可能会将true
(或例外)视为可能性(在我看来,答案仍然是否定的).是的,Sun当前实现中的单词也引起了我的注意。我花了几个小时研究JDK 1.6、1.5甚至1.4 javadocs,但它对我来说仍然毫无意义。我能想象的理解它的唯一方法是将Sun这个词视为一个拼写错误,并按照当前实现中的方式阅读——假设这是指当前的imple
list contains s:false
Calling CIString.equals from Polish
list contains a:true
void test(List<CIString> list, Object s) {
if (list != null && list.size() > 0) {
if (list.get(0).equals(s)) { // unsymmetric equality in CIString
assert !list.contains(s); // "usage code": list.contain(s)
}
}
}
List<CIString> wrapperWithCce(List<CIString> original,
Comparator<CIString> comparator) {
final TreeSet<CIString> treeSet = new TreeSet<CIString>(comparator);
treeSet.addAll(original);
return new ArrayList<CIString>() {
{ addAll(treeSet); }
@Override
public boolean contains(Object o) {
return treeSet.contains(o); // if o is String, will throw CCE
}
};
}