为什么 hamcrest 说字节 0 不等于 int 0?

Why does hamcrest say that a byte 0 is not equal to an int 0?

考虑以下使用标准 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 会失败?

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 被装箱到 TByteint 被装箱到 Integer(在对equalTo),但推断为 Number 以匹配 Matcher<? super T>.

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

中工作

发生这种情况是因为 intbyte 被装箱到 IntegerByte,因为 hamcrest 匹配器对对象而不是基元进行操作。所以你正在比较一个 Integer 和一个 Byte,而 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 匹配器功能强大,但主要用于(复杂的)对象断言。