Как переопределить метод equals в Java
Я пытаюсь переопределить метод равных в Java. У меня есть класс People
который в основном имеет 2 поля данных name
а также age
, Теперь я хочу переопределить equals
метод, чтобы я мог проверить между 2 объектами людей.
Мой код выглядит следующим образом
public boolean equals(People other){
boolean result;
if((other == null) || (getClass() != other.getClass())){
result = false;
} // end if
else{
People otherPeople = (People)other;
result = name.equals(other.name) && age.equals(other.age);
} // end else
return result;
} // end equals
Но когда я пишу age.equals(other.age)
Это дает мне ошибку, так как метод equals может сравнивать только String, а age - Integer.
Решение
я использовал ==
оператор, как предложено, и моя проблема решена.
12 ответов
//Written by K@stackru
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ArrayList<Person> people = new ArrayList<Person>();
people.add(new Person("Subash Adhikari", 28));
people.add(new Person("K", 28));
people.add(new Person("Stackru", 4));
people.add(new Person("Subash Adhikari", 28));
for (int i = 0; i < people.size() - 1; i++) {
for (int y = i + 1; y <= people.size() - 1; y++) {
boolean check = people.get(i).equals(people.get(y));
System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
System.out.println(check);
}
}
}
}
//written by K@stackru
public class Person {
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!Person.class.isAssignableFrom(obj.getClass())) {
return false;
}
final Person other = (Person) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
if (this.age != other.age) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 53 * hash + this.age;
return hash;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Выход:
бежать:
- Субаш Адхикари - ВС - К ложь
- Субаш Адхикари - VS - Stackru false
- Субаш Адхикари - VS - Субаш Адхикари есть
- K - VS - Stackru false
- K - VS - Субаш Адхикари ложь
- Переполнение стека - VS - Subash Adhikari false
- СТРОИТЬ УСПЕШНО (общее время: 0 секунд)
Прежде всего: вы не переопределяете equals
перегружаешь это.
Не видя фактической декларации age
Сложно сказать, почему вы получаете ошибку.
Я не уверен в деталях, так как вы не опубликовали весь код, но:
- не забудьте переопределить
hashCode()
также equals
метод должен иметьObject
неPeople
в качестве типа аргумента. В настоящий момент вы перегружаете, а не переопределяете метод equals, что, вероятно, не то, что вам нужно, особенно если учесть, что вы проверяете его тип позже.- ты можешь использовать
instanceof
чтобы проверить это объект People, напримерif (!(other instanceof People)) { result = false;}
equals
используется для всех объектов, но не для примитивов. Я думаю, что вы имеете в виду возрастint
(примитивно), в этом случае просто использовать==
, Обратите внимание, что Integer (с большой буквы "I") - это объект, который следует сравнивать с равными.
См. Какие проблемы следует учитывать при переопределении equals и hashCode в Java? Больше подробностей.
Пункт 10: Соблюдайте генеральный договор, когда переопределение равно
Согласно Effective Java, переопределение
equals
Метод кажется простым, но есть много способов ошибиться, и последствия могут быть ужасными. Самый простой способ избежать проблем - это не переопределятьequals
метод, в этом случае каждый экземпляр класса равен только себе. Это правильно, если применимо любое из следующих условий:
Каждый экземпляр класса по своей сути уникален. Это верно для таких классов, как Thread, которые представляют активные сущности, а не значения. Реализация equals, предоставляемая Object, имеет абсолютно правильное поведение для этих классов.
Классу не нужно предоставлять тест на "логическое равенство". Например, java.util.regex.Pattern мог бы переопределить equals, чтобы проверить, представляют ли два экземпляра Pattern одно и то же регулярное выражение, но дизайнеры не думали, что клиенты будут нуждаться в этих функциях или захотят их. При этих обстоятельствах реализация equals, унаследованная от Object, является идеальной.
Суперкласс уже переопределил equals, и поведение суперкласса подходит для этого класса. Например, большинство реализаций Set наследуют свою реализацию equals от AbstractSet, реализации List от AbstractList и реализации Map от AbstractMap.
Класс является приватным или закрытым для пакета, и вы уверены, что его метод equals никогда не будет вызываться. Если вы крайне склонны к риску, вы можете переопределить метод equals, чтобы убедиться, что он не вызывается случайно:
equals
Метод реализует отношение эквивалентности. У него есть эти свойства:
Рефлексивный: для любого ненулевого эталонного значения
x
,x.equals(x)
должен вернуть истину.Симметричный: для любых ненулевых опорных значений
x
а такжеy
,x.equals(y)
должен возвращать true тогда и только тогда, когда y.equals(x) возвращает true.Transitive: для любых ненулевых значений ссылки
x
,y
,z
, еслиx.equals(y)
возвращаетсяtrue
а такжеy.equals(z)
возвращаетсяtrue
, затемx.equals(z)
должен вернутьсяtrue
,Согласованно: для любых ненулевых ссылочных значений
x
а такжеy
несколько вызововx.equals(y)
должен последовательно возвращатьсяtrue
или последовательно вернутьfalse
при условии, что никакая информация, используемая в сравнениях равных, не изменяется.Для любого ненулевого ссылочного значения
x
,x.equals(null)
должен вернутьсяfalse
,
Вот рецепт высококачественного метода равных:
Использовать
==
оператор, чтобы проверить, является ли аргумент ссылкой на этот объект. Если так, верните истину. Это просто оптимизация производительности, но она того стоит, если сравнение потенциально дорого.Использовать
instanceof
оператор, чтобы проверить, имеет ли аргумент правильный тип. Если нет, верните false. Как правило, правильный тип - это класс, в котором встречается метод. Иногда это некоторый интерфейс, реализованный этим классом. Используйте интерфейс, если класс реализует интерфейс, который уточняет контракт равенства, чтобы разрешить сравнения между классами, которые реализуют интерфейс. Интерфейсы коллекции, такие как Set, List, Map и Map.Entry, имеют это свойство.Приведите аргумент к правильному типу. Поскольку этому приведению предшествовал экземпляр теста, он гарантированно будет успешным.
Для каждого "значимого" поля в классе проверьте, соответствует ли это поле аргумента соответствующему полю этого объекта. Если все эти тесты пройдены успешно, верните true; в противном случае верните false. Если тип в Шаге 2 является интерфейсом, вы должны получить доступ к полям аргумента через методы интерфейса; если тип является классом, вы можете получить доступ к полям напрямую, в зависимости от их доступности.
Для примитивных полей, тип которых не
float
или жеdouble
, использовать==
оператор для сравнения; для полей ссылки на объект, вызовитеequals
метод рекурсивный; заfloat
поля, используйте статическийFloat.compare(float, float)
Способ; и дляdouble
поля, используйтеDouble.compare(double, double)
, Специальная обработка полей с плавающей запятой и двойных полей необходима благодаря существованиюFloat.NaN
,-0.0f
и аналогичные двойные значения; Хотя вы могли бы сравнитьfloat
а такжеdouble
поля со статическими методамиFloat.equals
а такжеDouble.equals
это повлечет за собой автобокс при каждом сравнении, что будет иметь низкую производительность. Заarray
поля, примените эти рекомендации к каждому элементу. Если каждый элемент в поле массива является значимым, используйте один изArrays.equals
методы.Некоторые поля ссылки на объект могут на законных основаниях содержать
null
, Чтобы избежать возможностиNullPointerException
проверьте такие поля на равенство, используя статический методObjects.equals(Object, Object)
,// Class with a typical equals method public final class PhoneNumber { private final short areaCode, prefix, lineNum; public PhoneNumber(int areaCode, int prefix, int lineNum) { this.areaCode = rangeCheck(areaCode, 999, "area code"); this.prefix = rangeCheck(prefix, 999, "prefix"); this.lineNum = rangeCheck(lineNum, 9999, "line num"); } private static short rangeCheck(int val, int max, String arg) { if (val < 0 || val > max) throw new IllegalArgumentException(arg + ": " + val); return (short) val; } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof PhoneNumber)) return false; PhoneNumber pn = (PhoneNumber)o; return pn.lineNum == lineNum && pn.prefix == prefix && pn.areaCode == areaCode; } ... // Remainder omitted }
@Override
public boolean equals(Object that){
if(this == that) return true;//if both of them points the same address in memory
if(!(that instanceof People)) return false; // if "that" is not a People or a childclass
People thatPeople = (People)that; // than we can cast it to People safely
return this.name.equals(thatPeople.name) && this.age == thatPeople.age;// if they have the same name and same age, then the 2 objects are equal unless they're pointing to different memory adresses
}
При сравнении объектов в Java вы делаете семантическую проверку, сравнивая тип и определяя состояние объектов с:
- сам (тот же экземпляр)
- сам (клон или реконструированная копия)
- другие объекты разных типов
- другие объекты того же типа
null
Правила:
- Симметрия:
a.equals(b) == b.equals(a)
equals()
всегда даетtrue
или жеfalse
но никогдаNullpointerException
,ClassCastException
или любой другой бросаемый
Сравнение:
- Проверка типа: оба экземпляра должны быть одного типа, что означает, что вы должны сравнить фактические классы на равенство. Это часто не правильно реализуется, когда разработчики используют
instanceof
для сравнения типов (который работает только при отсутствии подклассов и нарушает правило симметрии, когдаA extends B -> a instanceof b != b instanceof a)
, - Семантическая проверка определения состояния: убедитесь, что вы понимаете, по какому состоянию идентифицируются экземпляры. Лица могут быть идентифицированы по номеру социального страхования, но не по цвету волос (можно покрасить), имени (можно изменить) или по возрасту (меняется постоянно). Только с объектами значений следует сравнивать полное состояние (все непереходные поля), в противном случае проверять только то, что идентифицирует экземпляр.
Для тебя Person
учебный класс:
public boolean equals(Object obj) {
// same instance
if (obj == this) {
return true;
}
// null
if (obj == null) {
return false;
}
// type
if (!getClass().equals(obj.getClass())) {
return false;
}
// cast and compare state
Person other = (Person) obj;
return Objects.equals(name, other.name) && Objects.equals(age, other.age);
}
Повторно используемый универсальный служебный класс:
public final class Equals {
private Equals() {
// private constructor, no instances allowed
}
/**
* Convenience equals implementation, does the object equality, null and type checking, and comparison of the identifying state
*
* @param instance object instance (where the equals() is implemented)
* @param other other instance to compare to
* @param stateAccessors stateAccessors for state to compare, optional
* @param <T> instance type
* @return true when equals, false otherwise
*/
public static <T> boolean as(T instance, Object other, Function<? super T, Object>... stateAccessors) {
if (instance == null) {
return other == null;
}
if (instance == other) {
return true;
}
if (other == null) {
return false;
}
if (!instance.getClass().equals(other.getClass())) {
return false;
}
if (stateAccessors == null) {
return true;
}
return Stream.of(stateAccessors).allMatch(s -> Objects.equals(s.apply(instance), s.apply((T) other)));
}
}
Для тебя Person
класс, используя этот служебный класс:
public boolean equals(Object obj) {
return Equals.as(this, obj, t -> t.name, t -> t.age);
}
Так как я угадываю age
имеет тип int
:
public boolean equals(Object other){
boolean result;
if((other == null) || (getClass() != other.getClass())){
result = false;
} // end if
else{
People otherPeople = (People)other;
result = name.equals(otherPeople.name) && age == otherPeople.age;
} // end else
return result;
} // end equals
tl;dr
record Person ( String name , int age ) {}
if(
new Person( "Carol" , 27 ) // Compiler auto-generates implicitly the constructor.
.equals( // Compiler auto-generates implicitly the `equals` method.
new Person( "Carol" , 42 )
)
) // Returns `false`, as the name matches but the age differs.
{ … }
Подробности
Пока ваша конкретная проблема решена (используя
==
для проверки равенства между
int
примитивные значения), есть альтернатива, которая избавляет от необходимости писать этот код.
Java 16 приносит запись функции.
Запись - это краткий способ написать класс, основной целью которого является прозрачная и неизменная передача данных. Компилятор неявно создает конструктор, геттеры, & и
toString
.
метод предоставляется автоматически
Неявный метод по умолчанию сравнивает каждое поле члена, объявленное вами для записи. Члены могут быть объектами или примитивами, оба типа автоматически сравниваются в методе по умолчанию.
Например, если у вас есть запись с двумя полями,
name
&
age
, оба этих поля автоматически сравниваются, чтобы определить равенство между парой
Person
объекты.
public record Person ( String name , int age ) {}
Попытайся.
Person alice = new Person( "Alice" , 23 ) ;
Person alice2 = new Person( "Alice" , 23 ) ;
Person bob = new Person( "Bob" , 19 ) ;
boolean samePerson1 = alice.equals( alice2 ) ; // true.
boolean samePerson2 = alice.equals( bob ) ; // false.
Вы можете переопределить метод записи, если хотите, чтобы поведение было отличным от значения по умолчанию. Но если вы переопределите
equals
, обязательно переопределите
hashCode
для согласованной логики, как и для обычного класса Java . И подумайте дважды: всякий раз, добавляя методы к, пересматривайте, действительно ли структура записи соответствует этой проблемной области.
Совет: A
record
может быть определен внутри другого класса и даже локально внутри метода.
Если age - int, вы должны использовать ==, если это объект Integer, тогда вы можете использовать equals(). Вам также нужно реализовать метод хеш-кода, если вы переопределяете equals. Подробности контракта доступны в javadoc Object, а также на различных страницах в Интернете.
Метод equals определяет параметр метода типа Object, а его возвращаемый тип - логический.
Don’t change the name of the method, its return type, or the type of method parameter
когда вы определяете (переопределяете) этот метод в своем классе для сравнения двух объектов.
public boolean equals(Object anObject) {
...
}
Вот решение, которое я недавно использовал:
public class Test {
public String a;
public long b;
public Date c;
public String d;
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Test)) {
return false;
}
Test testOther = (Test) obj;
return (a != null ? a.equals(testOther.a) : testOther.a == null)
&& (b == testOther.b)
&& (c != null ? c.equals(testOther.c) : testOther.c == null)
&& (d != null ? d.equals(testOther.d) : testOther.d == null);
}
}
Для ленивых программистов:lombok
библиотека очень проста и экономит время. пожалуйста, взгляните на эту ссылку вместо того, чтобы писать строки кодов и правил, вам просто нужно применить эту библиотеку в вашей среде IDE, а затем просто @Data, и все готово.
import lombok.Data;
@Data // this is the magic word :D
public class pojo {
int price;
String currency;
String productName;
}
на самом деле в приведенном выше коде @Data является ярлыком для
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@EqualsAndHashCode
@ToString
//or instead of all above @Data
public class pojo {
int price;
String currency;
String productName;
}