【问题标题】:Why does hamcrest say that a byte 0 is not equal to an int 0?为什么 hamcrest 说字节 0 不等于 int 0?
【发布时间】:2015-11-26 15:06:06
【问题描述】:

考虑以下使用标准 JUnit 断言和 hamcrest 的 assertThat 的测试用例:

byte b = 0;
int i = 0;

assertEquals(b, i); // success
assertThat(b, equalTo(i)); // java.lang.AssertionError: Expected: <0> but: was <0>

if (b == i) {
    fail(); // test fails, so b == i is true for the JVM
}

为什么会这样?对于 JVM,这些值显然是相等的,因为 b == itrue,那么为什么 hamcrest 会失败?

【问题讨论】:

  • 因为Byte.valueOf((byte) 0).equals(Integer.valueOf(0)) 是假的。
  • 如在上面的 assylias 示例中所见,字节被自动装箱成一个字节对象。如Hamcrest's equalTo docs 中所见,它使用 Object1.equals(Object2)。由于 byte 和 int 都是原语,因此它将它们自动装箱为 Byte 和 Integer 对象。 Byte1.equals(Integer1) 将返回 false,即使这些装箱对象的值相同。

标签: java junit hamcrest


【解决方案1】:

Assert#assertThat 是一个通用方法。原始类型不适用于泛型。在这种情况下,byteint 分别被装箱到 ByteInteger

然后变成(在assertThat内)

Byte b = 0;
Integer i = 0;

b.equals(i);

Byte#equals(Object) 的实现检查参数是否为Byte 类型,如果不是则立即返回false

另一方面,assertEqualsAssert#assertEquals(long, long),在这种情况下,byteint 参数都被提升为 long 值。在内部,这在两个相等的原始 long 值上使用了 ==


请注意,此装箱转换有效,因为 assertThat 被声明为

public static <T> void assertThat(T actual, Matcher<? super T> matcher) {

其中byte 被装箱到Byte 以用于T,而int 被装箱到Integer(在对equalTo 的调用中),但被推断为Number匹配Matcher&lt;? super T&gt;

这适用于 Java 8 改进的通用推理。您需要显式类型参数才能使其在 Java 7 中工作。

【讨论】:

    【解决方案2】:

    发生这种情况是因为 intbyte 被装箱为 IntegerByte,因为 hamcrest 匹配器对对象而不是基元进行操作。因此,您将IntegerByte 进行比较,Byte.equals() 的实现是:

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

    Integer.equals():

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

    换句话说,IntegerByte 总是不相等的。比较原语时,只需使用 Assert.assertEquals 代替。 hamcrest 匹配器功能强大,但主要用于(复杂)对象断言。

    【讨论】:

    • Java 有什么理由不检查同一范围内的值吗?例如。 if (obj instanceof Integer) { return ((Integer)obj).intValue() == (int) value;}Byte.equals()?
    • @sina 好吧,这可能就是我们可以使用原语的原因。 Java 比较两个对象时,首先检查两个对象是否属于同一类型;如果不是,它只会返回 false。 IntegerByte 是对象,所以同样适用于它们。如果(new Byte(0)).equals(new Integer(0)) 会返回true,那对我来说会有点奇怪。一个类似的例子是,如果(new Dog("Luke")).equals(new Cat("Luke")) 会返回 true,仅仅是因为它们具有相同的名称(我知道,这里不是最好的例子,但是你会看到如果它返回 true,它看起来多么奇怪)。
    • @KevinCruijssen 当拳击发生时,这是你不会想到的。有人可能会争辩说,Number 可能需要一个确实允许IntegerByte 具有可比性的等号,但这可能会再次导致FloatDouble 的意外行为或考虑到非常复杂的实现“其他”数字类型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-05
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    • 2012-11-11
    • 1970-01-01
    相关资源
    最近更新 更多