Подтвердить что ты не робот

Проверьте, полностью ли два объекта в Java

У меня есть класс Java, вот пример:

public class Car {

    private int fuelType;
    private Date made;
    private String name;
.
.
. // and so on

Теперь позвольте сказать, что у меня есть два автомобильных объекта, и я хочу сравнить, если все их переменные равны.

В настоящий момент я решил это путем переопределения метода equals(Object o), и я проверяю, совпадают ли все переменные в обоих объектах.

Проблема в том, что если у меня есть 20 классов, мне придется переопределить equals(Object o) в каждом из них.

Есть ли способ создать какой-то универсальный метод, который мог бы сравнить любой из двух объектов, которые я передаю ему, и сообщить мне, соответствуют ли они каждой переменной или нет?

4b9b3361

Ответ 1

У вас есть несколько вариантов автоматизации Equals и Hashcode (опция №3 BLEW MY MIND!):

  • Ваша IDE. Я бы не рекомендовал его для большинства объектов, так как они могут медленно дрейфовать с фактическим определением класса. Они также выглядят уродливыми и загрязняют вашу кодовую базу шаблоном кода.
  • Apache Commons имеет кучу вещей для упрощения, в том числе отражающая версия, поэтому не стоит рисковать устареванием с определением класса. Это лучше, чем # 1, если вам не нужен быстрый equals/hashcode, но все же слишком много шаблонов для моей симпатии.
  • Проект Ломбок и обработка аннотаций. Удалите EqualsAndHashCode аннотацию по классу ya и сделайте с ней. Я рекомендую использовать Project Lombok. Он добавляет волшебство в сборку (но не так много), поэтому для плагинов для вашей среды требуется плагин, но это небольшая цена, чтобы заплатить за отсутствие шаблона. Lombok - это обработчик аннотации, который выполняется во время компиляции, поэтому у вас нет производительности во время исполнения.
  • Использование другого языка, который поддерживает его, но также предназначен для JVM. Groovy использует аннотация и Kotlin поддерживает классы данных. Если бы ваш существующий код не был быстро преобразован, я бы избегал этого.
  • Google Auto имеет AutoValue. Как и Project Lombok, это процессор аннотаций, однако он имеет меньше магии за счет меньшего количества шаблонов (благодаря Louis Wasserman).

Ответ 2

вы можете использовать:

 org.apache.commons.lang.builder.CompareToBuilder.reflectionCompare(Object lhs, Object rhs);

он использует отражение для сравнения полей вот javadoc: javadoc

Ответ 3

Обычно вы можете генерировать методы equals/hashCode вашей IDE - все крупные игроки в этом поле способны к этому (Eclipse, IntelliJ Idea и Netbeans).

Как правило, вы можете создать код, который будет использовать отражение, но я не рекомендую этот подход, поскольку объективный подход более ясен и удобен в обслуживании. Также отражение будет не так быстро, как "стандартный" способ. Если вы действительно хотите пойти по этому пути, существуют такие утилиты, как EqualsBuilder и HashCodeBuilder.

Только для вашей информации существуют языки на основе JVM, которые уже поддерживают эти функции, например. Kotlin классы данных, которые могут быть довольно хорошо использованы в существующих проектах Java.

Ответ 4

Я возьму особое мнение большинству (используйте apache commons with reflection) здесь: Да, это немного код, который вы должны написать (пусть ваша IDE генерируется на самом деле), но вам нужно сделать это только один раз и число классов данных, которые должны реализовать equals/hashcode, как правило, довольно управляемо - по крайней мере во всех крупных проектах (250k + LOC), над которыми я работал.

Конечно, если вы добавите новый класс в класс, вам придется не забывать обновлять функции equals/hashcode, но, как правило, легко заметить, самое последнее во время просмотра кода.

И если честно, если вы используете простой небольшой вспомогательный класс, который даже в Java7, вы можете сократить код, который Wana Ant показал очень. На самом деле все, что вам нужно:

@Override
public boolean equals(Object o) {
    if (o instanceof Car) { // nb: broken if car is not final - other topic
        Car other = (Car) o;
        return Objects.equals(fuelType, other.fuelType) && 
               Objects.equals(made, other.made) && 
               Objects.equals(name, other.name);
    }
    return false;
}

похож на hashcode:

@Override
public int hashCode() {
    return Objects.hash(fuelType, made, name);
}

Не так короче, как решение отражения? Правда, но это просто, легко поддерживать, адаптировать и читать - и производительность на порядок лучше (что для классов, которые реализуют равные и хэш-коды часто важно)

Ответ 5

Я просто поставлю вилку для своего любимого решения этой проблемы: @AutoValue.

Это проект с открытым исходным кодом от Google, который предоставляет обработчик аннотации, который генерирует синтетический класс, который реализует для вас equals и hashCode.

Поскольку это автоматически сгенерированный код, вам не нужно беспокоиться о том, чтобы случайно забыть поле или испортить реализацию equals или hashCode. Но поскольку код генерируется во время компиляции, накладные расходы отсутствуют (в отличие от решений на основе отражений). Он также "API-невидим" - пользователи вашего класса не могут отличить тип @AutoValue и тип, который вы внедрили сами, и вы можете менять взад и вперед в будущем, не нарушая звонков.

См. также эту презентацию, которая объясняет обоснование и делает лучшую работу, сравнивая ее с другими подходами.

Ответ 6

Теоретически вы можете использовать отражение для создания своего рода утилиты, так как многие люди предлагают вам комментарии. Лично я не рекомендую вам это делать. вы закончите тем, что частично работает.

Многие вещи в Java полагаются на equal или hashCode, например метод contains, который вы можете найти во всем, что реализует Collection.

Рекомендуется переопределение equalhashCode). Кроме того, я думаю, что любая достойная IDE будет иметь возможность генерировать их для вас. Следовательно, вы можете сделать это быстрее, чем с помощью отражения.

Ответ 7

То, как я это сделаю:

@Override
public boolean equals(Object obj) {
    if (obj instanceof Car) {
        return internalEquals((Car) obj);
    }
    return super.equals(obj);
}

protected boolean internalEquals(Car other) {
    if(this==other){
        return true;
    }
    if (other != null) {
        //suppose fuelType can be Integer.
        if (this.getFuelType() !=null) {
            if (other.getFuelType() == null) {
                return false;
            } else if (!this.getFuelType().equals(other.getFuelType())) {
                return false;
            }
        } else if(other.getFuelType()!=null){
            return false;
        }
        if (this.getName() != null) {
            if (other.getName() == null) {
                return false;
            } else if (!this.getName().equals(other.getName())) {
                return false;
            }
        }
        else if(other.getName()!=null){
            return false;
        }
         if (this.getDate() != null) {
            if (other.getDate() == null) {
                return false;
            } else if (!this.getDate().getTime()!=(other.getDate().getTime())) {
                return false;
            }
        }
        else if(other.getDate()!=null){
            return false;
        }
        return true;
    } else {
        return false;
    }
}

РЕДАКТИРОВАТЬ
Упрощенная версия

     public class Utils{
          /**
           * Compares the two given objects and returns true, 
           * if they are equal and false, if they are not.
           * @param a one of the two objects to compare
           * @param b the other one of the two objects to compare
           * @return if the two given lists are equal.
           */
           public static boolean areObjectsEqual(Object a, Object b) {

               if (a == b){
                  return true;
               }
               return (a!=null && a.equals(b));
          }

          public static boolean areDatesEqual(Date a, Date b){
             if(a == b){
                return true;
             }
             if(a==null || b==null){ 
                return false;
             }
             return a.getTime() == b.getTime();
          }
   }

   @Override
   public boolean equals(other obj) {
      if(this == other){
         return true;
      } 
      if(other == null){
          return false;
      }
      if (other instanceof Car) {
          return internalEquals((Car) other);
      }
      return super.equals(obj);
   }

   protected boolean internalEquals(Car other) {        
        //suppose fuelType can be Integer.
        if (!Utils.areObjectsEqual(this.getName(), other.getName()){                   
            return false;
        }
        if (!Utils.areObjectsEqual(this.getName(), other.getName()){ 
            return false;
        }
        if (!Utils.areDatesEqual(this.getDate(), other.getDate()){ 
            return false;
        } 
        return true;
    }
}

Также не забывайте о hashcode, они кодируют рука об руку.