【问题标题】:Compare objects of different classes using reflection使用反射比较不同类的对象
【发布时间】:2019-02-14 15:36:16
【问题描述】:

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

class Bean1 {
   int id;
}

class Bean2 {
   int id;
}

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

此代码返回 false:

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

还有其他方法可以比较对象并忽略对象类吗?

【问题讨论】:

  • 你能提供更多关于这两个类的细节吗?你指的属性是什么?
  • 任何属性。我不想描述所有属性,因为解决方案需要通用。
  • 我想你现在应该可以接受你的回答了 ;-)

标签: java reflection


【解决方案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】:

    除非你找到一个库可以做到这一点,否则你总是可以编写自己的反射代码来满足你的需要:

    • 查询对象上的字段以进行比较
    • 比较它们的内容以防两组字段名称相同
    • 不考虑实际班级

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

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

    public Bean1 from(Bean2 incoming) { ...
    

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

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

    所以:如果你没有找到一个库来执行基于等于忽略类的反射,你应该能够通过 JSON 序列化来获取同一类的两个对象。

    【讨论】:

      【解决方案3】:

      您可以使用反射作为解决问题的方法,但最好的方法是如 @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
      

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

      【讨论】:

        【解决方案4】:

        如果您的实体具有公共属性,请使用抽象类并覆盖 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;
        }
        

        【讨论】:

        • 如上所述,我的类是生成的代码,而且我有很多属性,所以我不想写equal方法。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-10-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多