深反射比较等于

时间:2012-03-09 10:40:20

标签: java serialization reflection comparison equals

我正在尝试通过将结果对象与原始对象进行比较来验证序列化和反序列化例程。例程可以序列化任意和深度嵌套的类,因此我想要一个比较例程,可以给出原始和最终实例,并反过来遍历每个值类型并比较值,并迭代地深入参考类型以比较值。

我已经尝试过Apache Commons Lang EqualsBuilder.reflectionEquals(inst1, inst2),但这似乎没有进行非常深入的比较,它只是比较参考类型的平等而不是深入研究它们:

以下代码说明了我的问题。对reflectionEquals的第一次调用返回true,但第二次返回false。

是否有人可以推荐的图书馆例程?

class dummy {
    dummy2 nestedClass;
}

class dummy2 {
    int intVal;
}

@Test
public void testRefEqu() {

    dummy inst1 = new dummy();
    inst1.nestedClass = new dummy2();
    inst1.nestedClass.intVal = 2;
    dummy inst2 = new dummy();
    inst2.nestedClass = new dummy2();
    inst2.nestedClass.intVal = 2;
    boolean isEqual = EqualsBuilder.reflectionEquals(inst1.nestedClass, inst2.nestedClass);
    isEqual = EqualsBuilder.reflectionEquals(inst1, inst2);
}

5 个答案:

答案 0 :(得分:13)

从这个问题的答案https://stackoverflow.com/a/1449051/116509和一些初步测试中,看起来Unitil'ReflectionAssert.assertReflectionEquals做了你所期待的。 (编辑:但可能会被放弃,因此您可以尝试AssertJ https://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#field-by-field-recursive

我对EqualsBuilder的这种行为感到非常震惊,所以感谢这个问题。在这个网站上有很多答案推荐它 - 我想知道推荐它的人是否意识到这样做了?

答案 1 :(得分:4)

一种方法是使用反射比较对象 - 但这很棘手。另一种策略是比较序列化对象的字节数组:

class dummy implements Serializable {
    dummy2 nestedClass;
}

class dummy2  implements Serializable {
    int intVal;
}

@Test
public void testRefEqu() throws IOException {

    dummy inst1 = new dummy();
    inst1.nestedClass = new dummy2();
    inst1.nestedClass.intVal = 2;

    dummy inst2 = new dummy();
    inst2.nestedClass = new dummy2();
    inst2.nestedClass.intVal = 2;

    boolean isEqual1 = EqualsBuilder.reflectionEquals(inst1.nestedClass, inst2.nestedClass);
    boolean isEqual2 = EqualsBuilder.reflectionEquals(inst1, inst2);

    System.out.println(isEqual1);
    System.out. println(isEqual2);

    ByteArrayOutputStream baos1 =new ByteArrayOutputStream();
    ObjectOutputStream oos1 = new ObjectOutputStream(baos1);
    oos1.writeObject(inst1);
    oos1.close();

    ByteArrayOutputStream baos2 =new ByteArrayOutputStream();
    ObjectOutputStream oos2 = new ObjectOutputStream(baos2);
    oos2.writeObject(inst1);
    oos2.close();

    byte[] arr1 = baos1.toByteArray();
    byte[] arr2 = baos2.toByteArray();

    boolean isEqual3 = Arrays.equals(arr1, arr2);

    System.out.println(isEqual3);

}

您的应用程序序列化和反序列化对象,因此这种方法似乎是解决问题的最快解决方案(就CPU操作而言)。

答案 2 :(得分:0)

在相关类上实现equals()方法。每次调用的等于将比较嵌套类的相等性(或者,如果您愿意,将比较数据成员的相等性)。正确编写的equals方法总是会导致深度比较。

在您的示例中,dummy类等于:

public boolean equals(Object other)
{
    if (other == this) return true;
    if (other instanceOf dummy)
    {
        dummy dummyOther = (dummy)other;
        if (nestedClass == dummyOther.nestedClass)
        {
           return true;
        }
        else if (nestedClass != null)
        {
           return nestedClass.equals(dummyOther);
        }
        else // nestedClass == null and dummyOther.nestedClass != null.
        {
           return false;
        }
    }
    else
    {
      return false;
    }
}

答案 3 :(得分:0)

您可以使用AssertJ的field by field recursive comparison功能,例如:

import static org.assertj.core.api.BDDAssertions.then;

then(actualObject).usingRecursiveComparison().isEqualTo(expectedObject);

答案 4 :(得分:0)

我知道这是一个较晚的答复,但这对有需要的人可能有用。因此,在我的项目中,我需要比较整个反序列化值集,以查看它们是否正确匹配,而我使用的dep是Javers。它与SpringBoot很好地集成在一起,用法非常简单。

以下是示例展示:

@RunWith(SpringRunner.class)
public class DeserializerTest {

    private HubMessage message;
    private static final String EXPECTED_INVALID_MSG = "testInvalidMessage";
    private PlatformEventHeader msgHeaders;
    private ObjectMapper objectMapper = new ObjectMapper();
    private final DeserializerClass testDeserializerClass = new DeserializerClass();
    private String TOPIC_TEST = "testTopic";
    private static final String ACTUAL_HUBMESSAGE = "XXXStringValueofmessage";
    @Before
    public void stubSetup() throws IOException {
        // setup the message
       message = new HubMessage ();

        //setup the headers
       msgHeaders = new PlatformEventHeader();
        ...
        ...

        DomainEvent event= new DomainEvent();
        Map<String, String> msgEventObjMap = new HashMap<>();
        ...
        event.setMessage(msgEventObjMap);
        message.setEvent(event);
        message.setHeader(msgHeaders);

    }

    @Test
   public void test_deserialize_validMessage() throws JsonProcessingException {
          // My actual deserialzierClass
          HubMessage messageReceived = testDeserializerClass.deserialize(TOPIC_TEST,  
             new RecordHeaders(),ACTUAL_HUBMESSAGE .getBytes());

        Javers javers = JaversBuilder.javers().build();
        Diff diff = javers.compare((Object)objectMapper.convertValue(messageReceived, Object.class),
                (Object)objectMapper.readValue(ACTUAL_HUBMESSAGE, Object.class));
        Assert.assertEquals(diff.getChanges().size() , 0);
    }

我使用Diff来识别对象中是否有任何更改。现在,重写/使用toString()效率不高,因为它期望属性与Matchers中的期望值和实际值的顺序相同。 Unitils解决了此问题,可能会导致许多过渡下载的第三方jar引起很多麻烦。因此,更好的选择是使用Javers.io