Проверьте, полностью ли два объекта в Java
У меня есть класс Java, вот пример:
public class Car {
private int fuelType;
private Date made;
private String name;
.
.
. // and so on
Теперь позвольте сказать, что у меня есть два автомобильных объекта, и я хочу сравнить, если все их переменные равны.
В настоящий момент я решил это путем переопределения метода equals(Object o)
, и я проверяю, совпадают ли все переменные в обоих объектах.
Проблема в том, что если у меня есть 20 классов, мне придется переопределить equals(Object o)
в каждом из них.
Есть ли способ создать какой-то универсальный метод, который мог бы сравнить любой из двух объектов, которые я передаю ему, и сообщить мне, соответствуют ли они каждой переменной или нет?
Ответы
Ответ 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
.
Рекомендуется переопределение equal
(и hashCode
). Кроме того, я думаю, что любая достойная 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, они кодируют рука об руку.