仅基于commons属性动态比较两个不同类的对象

时间:2019-03-06 02:04:36

标签: java reflection

我正在编写一些完全动态的方法,其中需要比较两个不同类的对象。

以下是对象的示例:

public class Object1 {

    private String lastname;
    private String firstname;
    private int age;
    private int gender;

    //All getters and setters
}

public class Object2 {

    private String lastname;
    private String address;
    private String job;

    //All getters and setters

}

如您在此处看到的,唯一的公共属性是姓氏,所以我希望我的比较仅适用于姓氏

另外:

  • 在我的真实代码中,我使用了许多不同的类,我无法使它们实现通用接口,实际上,我根本无法修改它们
  • 我不知道哪个可能是commons属性,所以我无法对测试进行硬编码
  • 我正在使用Java 8

因此,我正在寻找类似于BeanUtils的类,该类具有用于通用属性的copyProperties方法,但是在这里,我不想比较复制。

我认为这种实用程序类可能存在,但找不到。

所以,如果您有个主意,我会很乐意阅读:)

谢谢!

[编辑1]有关为什么要这样做的更多信息:

我正在编写一个通用系统,以基于JAX-RS(Jersey)为REST Api生成端点。 我正在使用具有类似«simple»示例之类的泛型类型的接口:

public interface sampleEndpoint<BEANPARAM,BODYREQUEST,RESPONSE>  {

    @PUT
    @Path("/{id}")
    default RESPONSE update(@Valid @BeanParam BEANPARAM bp, @Valid BODYREQUEST body) {
        //Check if id in path is the same as id in the body

        ....
    }


}

(对于Jersey,我们不能使用BeanParam在同一个Bean PathParam,QueryParam和RequestBody中进行检索。这就是为什么我需要同时使用BeanParam和另一个Bean作为主体)

我的用例可能更复杂,但这只是一个简单的例子。

2 个答案:

答案 0 :(得分:0)

根据我的收集,看来Reflection API正是您所需要的。您可以获取每个类中所有Fields的列表,然后仅比较两个具有相同名称的字段的值。

此方法的主要优点是可以将其推广到任意两个类。但是,Reflection常常令人讨厌,因为它破坏了封装,我建议您在解决此问题之前,针对您的问题寻求更好的解决方案。

答案 1 :(得分:0)

我最终创建了自己的课堂来实现我想要的。

我决定使用jackson(fasterxml)在JsonNode中转换我的对象,然后递归比较JsonNodes。

这是代码(我需要做一些测试以更深入地验证它,但它适用于我的用例):

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Iterator;

public class ObjectComparator {


    /**
     * For each attribute of «expected» Object, verify if this attribute exists in «actual» and if it exists, verify if the value is the same as the «expected» one
     *
     * @param expected : the reference JsonNode
     * @param actual : the Object in which we want to verify the attributes
     * @return true if all commons attributes between «expected» and «actual» have same values, otherwise returns false
     */
    public static boolean CommonsAttributesComparator(Object expected, Object actual) {

        ObjectMapper mapper = new ObjectMapper();
        JsonNode expectedNode = mapper.convertValue(expected, JsonNode.class);
        JsonNode actualNode = mapper.convertValue(actual, JsonNode.class);

        return(JsonNodeComparator(expectedNode, actualNode));
    }


    /**
     * For each attribute of «expected» JsonNode, verify if this attribute exists in «actual» and if it exists, verify if the value is the same as the «expected» one
     *
     * @param expectedNode : the reference JsonNode
     * @param actualNode : the JsonNode in which we want to verify the attributes
     * @return true if all commons attributes between «expected» and «actual» have same values, otherwise returns false
     */
    public static boolean JsonNodeComparator(JsonNode expectedNode, JsonNode actualNode) {

        Iterator<String> expectedKeys =  expectedNode.fieldNames();
        if(!expectedKeys.hasNext()) {
            return expectedNode.equals(actualNode);
        }
        while (expectedKeys.hasNext()) {
            String currentKey = expectedKeys.next();
            if (
                    !expectedNode.get(currentKey).isNull()
                            && actualNode.has(currentKey)
                            && !actualNode.get(currentKey).isNull()) {

                if (expectedNode.get(currentKey).isArray()) {
                    if (actualNode.get(currentKey).isArray()
                    && actualNode.get(currentKey).size() == expectedNode.get(currentKey).size()) {

                        boolean subNodeComparisonSucceeded = false;
                        for (final JsonNode expectedSubNode : expectedNode.get(currentKey)) {
                            for (final JsonNode actualSubNode : actualNode.get(currentKey)) {
                                subNodeComparisonSucceeded = JsonNodeComparator(expectedSubNode, actualSubNode);
                                if(subNodeComparisonSucceeded) {
                                    break;
                                }
                            }
                            if(!subNodeComparisonSucceeded) {
                                return false;
                            }
                        }
                    } else if(expectedNode.get(currentKey).size() > 0) {
                        return false;
                    }
                } else if(!expectedNode.get(currentKey).equals(actualNode.get(currentKey))) {
                    return false;
                }
            }
        }
        return true;
    }
}