equals方法中的等价性

3
为什么Integer.valueOf(42).equals(Long.valueOf(42))会失败呢? 是否实现equals必须是“自反,对称,传递,一致”的,但不必合理? :)

3
它们不属于同一类型,因此它们不能作为相等匹配。 Integer对象如何知道可能被实现的所有各种数字类型并为它们编写代码。这是不合理的。 - Don Branson
嗯,我想Java的设计者在这方面做错了(这与Sotirios和我上次谈论的内容太相似了)。我期望任何等于42的数字都应该与另一个等于42的数字相等,无论它们的类型如何。然而,这种情况是他们事后无法修复的。纠正这种行为可能会破坏一些东西。 - Dawood ibn Kareem
@SotiriosDelimanolis - 但是你所写的数字类型呢?我猜它们都可以实现一些接口。随着类型数量的增加,任何一种方法都会产生处理所有不同类型的巨大负担。 - Don Branson
@DonBranson 我并不是在说他们应该这样做,而是这是可能的。在Java中,Number类是所有一般数字类型(如IntegerLongDouble等)的父类,还包括BigIntegerBigDecimal。因此,它们都必须实现abstract intValue()方法。这个方法是可用的。Integer也可以使用它。 - Sotirios Delimanolis
是的,也许可以找到一个好的整数解决方案,但是我不敢想象double等于BigDecimal意味着什么。 - Dawood ibn Kareem
显示剩余3条评论
4个回答

5

根据Integer.equals()文档:

当且仅当参数不为null并且是包含与此对象相同int值的Integer对象时,结果为真。

换句话说,无论所表示的值如何,Integer永远不会测试为Long(或任何其他不是Integer的东西)等于equals()。如果您只是想比较原始值,请直接比较它们。

不允许跨类比较的一个好理由是:你该如何做呢?假设您决定实现Integer.equals()以测试其他对象是否为Number的实例,并使用other.intValue()(所有Number类都支持)来比较值。不幸的结果将是:

Integer.valueOf(42).equals(Long.valueOf(((Integer.MAX_VALUE + 1L) << 1) + 42))

因为Long.intValue()只是将值强制转换为int,所以会测试true

(int) ((Integer.MAX_VALUE + 1L) << 1) + 42)

这段代码的结果为42,因为它只考虑了低32位的值。更糟糕的是,如果你在Long.equals()中使用相同的逻辑(其中它测试所有使用longValue()Number对象),那么equals()测试将不再对称。


1
提醒一下,IntegerLong 是原始类型 intlong 的对象包装类。
以下是 Integerequals 方法的源代码(这里的 value 是一个 int 原始类型)。
public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

以下是 Long 类中 equals 方法的源代码(这里的 value 是一个 long 原始类型)。

public boolean equals(Object obj) {
    if (obj instanceof Long) {
        return value == ((Long)obj).longValue();
    }
    return false;
}

从源代码中,equals方法首先检查传入的obj是否是相应类的实例。如果不是,则equals跳过对原始value的检查并返回false
所以正如@Ted Hopp建议的那样,Integer永远不会等于Long(或任何其他不是Integer的东西)。建议比较原始值。
如果您仍然希望在对象包装器设置中执行等价性检查,则可以扩展您的整数(避免溢出),并让Java编译器为您做一些auto-(un)boxing。像这样...
Long theLong = Long.valueOf(42);
Integer theInteger = Integer.valueOf(42);
Long theIntegerInLong = (long) theInteger;
System.out.println(theLong.equals(theIntegerInLong) ? "Equal" : "Unequal");

-1

.equals() 用于在相同继承层次结构中的对象之间进行比较,包括最终超类 Object。虽然 Integer 和 Long 类都继承自 java.lang.Number,但 Number 类显然缺少一个比较 Number 实例值的 .equals() 方法。Integer 和 Long 的唯一共享的 .equals() 方法在 Object 中。Object-to-Object 比较方法不基于数字的值进行评估,因此打印出了看似奇怪的结果。

类 Integer 的文档说: .equals():"当且仅当参数不为 null 并且是包含与此对象相同 int 值的 Integer 对象时,结果为 true。" 它不将 Long 值视为更多的对象。

显然,根据 @David_Wallace 的说法,其他类层次结构在其超类中实现了 .equals()。new ArrayList().equals(new LinkedList()) 返回 true,并且两者都继承自类 java.util.AbstractList,该类具有其自己的 .equals() 实现


抱歉,已修复。谢谢。 - La-comadreja

-1
可能可以这样定义包装类之间的等式测试:尽管底层类型不同,但封装相同数字值的两个包装对象将比较相等。然而,这样做要么需要所有可能比较相等的类型实例都了解彼此,要么需要定义一种方式,使得对象能够与尚未被发明的其他对象进行比较。采用前一种方法意味着即使新的数字类型能够表示值5,一个等于5的Integer也不能报告自己等于另一个也表示5的新类型实例。采用后一种方法将允许新对象与值为5的Integer比较相等,但需要添加相对较少的好处所带来的巨大复杂性。
假设,例如,已经定义了一个BigDecimal和一个BigRational类;前者将数字存储为一个带有十进制幂的BigInteger;后者存储表示约分形式分数的一对BigInteger值。可以指定所有表示有理数的数字值应通过将它们转换为BigRational并比较结果来进行比较。由于每个有理数都有唯一的表示,这样定义的比较方法会表现为等价关系。不幸的是,这会非常缓慢。在大多数情况下,将比较对象转换成其他形式会更有意义,但是尝试为任意类型的组合识别最佳公共形式将会很困难。简单地说,不同类型就足以证明这些对象是“不同”的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接