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

2022-09-02 00:51:56

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

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,这些值显然是相等的,因为 is ,那么为什么会失败呢?b == itruehamcrest


答案 1

Assert#assert这是一个通用方法。基元类型不适用于泛型。在本例中,和 分别装箱到 和 。byteintByteInteger

然后它成为(在assertThat)

Byte b = 0;
Integer i = 0;

b.equals(i);

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

另一方面,是 Assert#assertEquals(long,long),在这种情况下,和 参数都提升为值。在内部,这使用两个相等的基元值。assertEqualsbyteintlong==long


请注意,此装箱转换之所以有效,是因为声明为assertThat

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

其中 ,被框定为 for ,而 the 被装箱到 an(在调用中),但推断为 a 以匹配 .byteByteTintIntegerequalToNumberMatcher<? super T>

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


答案 2

发生这种情况是因为 和 被装箱到 hamcrest 匹配器上,并且作为 hamcrest 匹配器在对象上操作,而不是在基元上操作。因此,您将 a 与 a 进行比较,并且 的实现是:intbyteIntegerByteIntegerByteByte.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;
}

换句话说,和 总是不相等的。比较基元时,只需改用。Hamcrest匹配器功能强大,但主要用于(复杂的)对象断言。IntegerByteAssert.assertEquals


推荐