Сравните объекты разных классов, используя отражение - PullRequest
0 голосов
/ 05 февраля 2019

У меня есть два объекта с одинаковыми атрибутами.Не спрашивайте меня, почему, это сгенерированный код, и я должен разобраться с этим.

class Bean1 {
   int id;
}

class Bean2 {
   int id;
}

Я хочу сравнивать объекты каждого класса без написания кода сравнения для каждого атрибута. В этой теме объясняется, как сравнивать два объекта, но в моем случае это не удается, поскольку они не принадлежат одному и тому же классу.

Этот код возвращает значение false:

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

Есть ли другой способ объединять объекты и игнорировать классы объектов?

Ответы [ 4 ]

0 голосов
/ 14 февраля 2019

Вы можете использовать отражение в качестве обходного пути для вашей проблемы, но лучший способ сделать это будет, как сказал @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

Если вы планируете использовать этот код, пожалуйста, проверьте крайние случаи, так как я этого еще не сделал

0 голосов
/ 05 февраля 2019

Я наконец смог сделать это с помощью ReflectionToStringBuilder Apache Common Lang, поскольку он предоставляет опцию 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());
0 голосов
/ 06 февраля 2019

Если у ваших сущностей есть общие атрибуты, используйте абстрактный класс и переопределите метод 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;
}
0 голосов
/ 05 февраля 2019

Если вы не найдете библиотеку, которая делает это, вы всегда можете написать свой собственный код отражения, который делает то, что вам нужно:

  • запросить поля в ваших объектах для сравнения
  • сравнитьих содержание в случае, если оба набора имен полей равны
  • , не принимая во внимание фактический класс

Если вы действительно имеете дело с такими "простыми" классами бинов, реализация этого самостоятельно можетна самом деле не будет слишком большой сделки.

Кроме того, вы можете создать генератор, подобный

public Bean1 from(Bean2 incoming) { ...

Если вы знаете свои классы, вы можете «вытянуть» все значения из Bean2 в экземпляр Bean1, а затем сравнить дваЭкземпляры Bean1.

Но, конечно, эта идея бесполезна (она требует от вас написания кода для обработки всех полей или повторного использования отражения), но мы можем легко расширить его до способа без отражения:сериализовать ваш Bean2 в JSON, используя, например, GSON.Затем удалите сериализацию этой строки JSON в Bean1!

Итак: если вы не найдете библиотеку, которая делает это отражение на основе равного, игнорируя класс, вы должны иметь возможность сериализации JSON, чтобы получить два объекта одного класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...