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

Java 仅基于公共属性动态比较两个不同类的对象,java,reflection,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 lastn

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

以下是对象的示例:

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

}
正如您在这里看到的,惟一的公共属性是lastname,因此我希望我的比较只适用于lastname

此外:

  • 在我的实际代码中,我使用了很多不同的类,我不能让它们实现公共接口,事实上,我根本不能修改它们
  • 我不知道哪些可能是commons属性,所以我不能硬编码我的测试
  • 我正在使用Java8
所以我在寻找一些类,类似于BeanUtils,它有一个copyProperties方法用于公共属性,但在这里,我想比较一下,而不是复制

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

因此,如果你有什么想法,我很乐意阅读:)

谢谢

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

我正在编写一个通用系统,为基于JAX-RS(Jersey)的RESTAPI生成端点。 我正在使用泛型类型的接口,如«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

        ....
    }


}
公共接口采样端点{
@放
@路径(“/{id}”)
默认响应更新(@Valid@BeanParam-BeanParam-bp,@Valid-BODYREQUEST-body){
//检查路径中的id是否与主体中的id相同
....
}
}
(对于Jersey,我们不能在同一个Bean PathParam、QueryParam和RequestBody中使用BeanParam进行检索……这就是为什么我需要同时使用BeanParam和另一个Bean作为主体的原因)


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

从我收集的信息来看,它似乎正是您所寻找的。您可以获得每个类中所有字段的列表,然后仅当两个字段的名称相同时才比较它们的值


这种方法的主要好处是,它可以推广到任何两个类。然而,反射经常被反对,因为它破坏了封装,我建议您在解决这个问题之前,针对您的问题寻找更好的解决方案。

从我收集的信息来看,反射似乎正是您所寻找的。您可以获得每个类中所有字段的列表,然后仅当两个字段的名称相同时才比较它们的值


这种方法的主要好处是,它可以推广到任何两个类。然而,反射经常被反对,因为它破坏了封装,我建议您在解决这个问题之前,针对您的问题寻找更好的解决方案。

我最终创建了自己的类来实现我想要的

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

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

import com.fasterxml.jackson.databind.JsonNode;
导入com.fasterxml.jackson.databind.ObjectMapper;
导入java.util.Iterator;
公共类对象比较器{
/**
*对于«预期»对象的每个属性,验证该属性是否存在于«实际»中,如果存在,验证该值是否与«预期»值相同
*
*@param应为:引用JsonNode
*@param-actual:我们要在其中验证属性的对象
*@如果«预期»和«实际»之间的所有公共属性具有相同的值,则返回true,否则返回false
*/
公共静态布尔CommonAttributesComparator(预期对象,实际对象){
ObjectMapper mapper=新的ObjectMapper();
JsonNode expectedNode=mapper.convertValue(应为JsonNode.class);
JsonNode actualNode=mapper.convertValue(实际,JsonNode.class);
返回(JsonNodeComparator(expectedNode,actualNode));
}
/**
*对于«预期»JsonNode的每个属性,验证该属性是否存在于«实际»中,如果存在,验证该值是否与«预期»值相同
*
*@param expectedNode:引用JsonNode
*@param actualNode:我们要在其中验证属性的JsonNode
*@如果«预期»和«实际»之间的所有公共属性具有相同的值,则返回true,否则返回false
*/
公共静态布尔JsonNodeComparator(JsonNode expectedNode,JsonNode actualNode){
迭代器expectedKeys=expectedNode.fieldNames();
如果(!expectedKeys.hasNext()){
返回expectedNode.equals(实际节点);
}
while(expectedKeys.hasNext()){
字符串currentKey=expectedKeys.next();
如果(
!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()){
布尔子节点comparisonsucceeed=false;
for(最终JsonNode expectedSubNode:expectedNode.get(currentKey)){
for(最终JsonNode actualSubNode:actualNode.get(currentKey)){
SubNodeComparisonSuccessed=JSONNodeCompariator(expectedSubNode,actualSubNode);
如果(子节点比较已成功){
打破
}
}
如果(!SubNodeComparisonSuccessed){
返回false;
}
}
}else if(expectedNode.get(currentKey).size()>0){
返回false;
}
}埃尔
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;
    }
}