使用反射比较不同类的对象

时间:2019-02-05 15:45:01

标签: java reflection

我有两个对象具有完全相同的属性。别问我为什么,这是生成的代码,我必须处理。

class Bean1 {
   int id;
}

class Bean2 {
   int id;
}

我想比较每个类的对象,而不必为每个属性编写比较代码。 This thread解释了如何比较两个对象,但是在我的案例中,这失败了,因为它们不是同一类的实例。

此代码返回false:

EqualsBuilder.reflectionEquals(new Bean1(1), new Bean2(1));

还有另一种比较对象并忽略对象类的方法吗?

4 个答案:

答案 0 :(得分:1)

除非找到可以执行此操作的库,否则您始终可以编写自己的反射代码来满足您的需要:

  • 查询对象上的字段以进行比较
  • 在两组字段名称相等的情况下比较它们的内容
  • 不考虑实际的课程

如果您真的在处理这样的“简单” bean类,那么自己实现可能就没什么大不了了。

或者,您可以创建类似的生成器

public Bean1 from(Bean2 incoming) { ...

如果您知道自己的类,则可以将Bean2中的所有值“拉”入Bean1实例,然后可以比较两个Bean1实例。

但是,这个想法当然无济于事(它要求您编写代码来处理所有字段,或者再次使用反射),但是我们可以轻松地将其扩展为非反射方式:将Bean2序列化为JSON,例如使用GSON。然后将该JSON字符串反序列化为Bean1!

因此:如果您找不到一个忽略该类的基于反射的库,则应该能够进行JSON序列化以获取相同类的两个对象。

答案 1 :(得分:1)

我终于可以使用Apache Common Lang的ReflectionToStringBuilder来做到这一点,因为它提供了ToStringStyle.NO_CLASS_NAME_STYLE选项。

此外,当我在JUnit测试中使用它时,它很有用,因为如果对象不同,我将能够看到哪个字段不同。

Assert.assertEquals(
   new ReflectionToStringBuilder(bean1, ToStringStyle.NO_CLASS_NAME_STYLE).toString(),
   new ReflectionToStringBuilder(bean2, ToStringStyle.NO_CLASS_NAME_STYLE).toString());

答案 2 :(得分:0)

如果您的实体具有公共属性,请使用抽象类并覆盖equals方法

abstract class Bean {
   private int id;


   public int getId(){
      return id;
   }    

   @Override
   boolean equals(Object obj){
      if(obj is null){
        return false; 
      } else if (obj instanceof Bean) {
          return ((Integer)this.getId()).equals((Bean)obj).getId());
      } else {
         return false;
      }
   }
}

class Bean1 extends Bean {
   //int id;
}

class Bean2 extends Bean  {
   //int id;
}

答案 3 :(得分:0)

您可以使用反射作为解决问题的方法,但是最好的方法是使用@imperezivan所说的,使用共同祖先将属性放在同一位置。

反射实现就像获取每个对象的所有字段并比较其值一样容易。这里有一个例子:

public static boolean compareObjects(Object o1, Object o2) {        
        try {
            List<Field> fields1 = getFields(o1);
            List<Field> fields2 = getFields(o2);
            boolean found;
            Field field2Temp = null;
            for (Field field1 : fields1) {
                found = false;
                for (Field field2 : fields2) {
                    if (field1.getName().equals(field2.getName())) {
                        if (!field1.get(o1).equals(field2.get(o2))) {
                            System.out.println("Value " + field1.get(o1) + " for field " + field1 + " does not match the value " + field2.get(o2) + " for field " + field2);
                            return false;
                        }
                        field2Temp = field2;
                        found = true;
                    }
                }
                if (!found) {
                    System.out.println("Field " + field1 + " has not been found in the object " + o2.getClass());
                    return false;
                } else {
                    fields2.remove(field2Temp);
                }
            }
            if (fields2.size() > 0) {
                for (Field field : fields2) {
                    System.out.println("Field " + field + " has not been found in the object " + o1.getClass());
                }
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    private static List<Field> getFields(Object o) {
        Field[] fields = o.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
        }
        return new ArrayList<>(Arrays.asList(fields));
    }

如您所见,我正在强制字段匹配,因此,如果在另一个对象中未找到一个字段,则该方法将返回false,但是您可以根据需要轻松地对其进行更改。

此代码详细介绍了如果两个对象不同,问题出在哪里。

执行此操作:

public static void main(String args[]){
        Bean1 bean1 = new Bean1(1);
        Bean2 bean2 = new Bean2(1);
        System.out.println("Equals? " + bean1.equals(bean2));
        System.out.println("Reflection equals? " + compareObjects(bean1, bean2));
        bean2 = new Bean2(2);
        System.out.println("Equals? " + bean1.equals(bean2));
        System.out.println("Reflection equals? " + compareObjects(bean1, bean2));
    }

您得到的结果是:

Equals? false
Reflection equals? true
Equals? false
Value 1 for field private int com.test.so.Bean1.id does not match the value 2 for field private int com.test.so.Bean2.id
Reflection equals? false

如果您打算使用此代码,请测试边缘情况,因为我还没有这样做