В этом посте я изложу свое понимание методов Переопределение
Apache Commons предоставляет два замечательных вспомогательных класса для вызова методов
hashCode()
и equals()
. Я хочу рассказать об их реализации по умолчанию, а также о том как корректно переопределить их. Я также напишу о реализации этих методов, используя Apache Common package’s вспомогательные классы.
Содержание этого поста:
- Использование
hashCode()
иequals()
. - Переопределение поведения по умолчанию.
- Переопределение
hashCode()
иequals()
, используя Apache Commons Lang. - То, что важно помнить.
- Особое Внимание При Использовании ORM.
hashCode()
и equals()
были определены в классе Object
, который является родительским классом для объектов java. Поэтому все java объекты наследуют от этих методов реализацию по умолчанию.Использование hashCode() и equals()
МетодhashCode()
используется для получения уникального целого номера для данного объекта. Когда необходимо сохранить объект как структуру данных в некой хэш-таблице (такой объект также называют корзиной — bucket), этот номер используется для определения его местонахождения в этой таблице. По умолчанию, метод hashCode()
для объекта возвращает номер ячейки памяти, где объект сохраняется.
Метод equals()
, как и следует из его названия, используется для простой проверки равенства двух объектов. Реализация этого метода по умолчанию просто проверяет по ссылкам два объекта на предмет их эквивалентности.
Переопределение поведения по умолчанию
Все работает отлично до тех пор, пока вы не переопределяете ни один из этих методов в своих классах. Но иногда в приложениях необходимо изменять поведение по умолчанию некоторых объектов. Давайте возьмем пример, где в вашем приложении имеется объектEmployee
. Давайте напишем минимально возможную структуру такого класса.
public class Employee
{
private Integer id;
private String firstname;
private String lastName;
private String department;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
}
Описанный выше класс Employee
имеет некоторые основные атрибуты и методы доступа. Сейчас рассмотрим простую ситуацию, где необходимо сравнить два объекта класса Employee
.
public class EqualsTest {
public static void main(String[] args) {
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
//Печатает false в консоли
System.out.println(e1.equals(e2));
}
}
Не нужно быть ясновидящим, чтобы догадаться, что написанный выше метод вернет “false”. Но правильно ли это на самом деле, учитывая, что эти оба объекта одинаковые? В real time application метод должен вернуть true.
Чтобы достигнуть корректного поведения, нам нужно переопределить метод equals()
, как и сделано ниже:
public boolean equals(Object o) {
if(o == null)
{
return false;
}
if (o == this)
{
return true;
}
if (getClass() != o.getClass())
{
return false;
}
Employee e = (Employee) o;
return (this.getId() == e.getId());
}
Добавьте этот метод в свой класс Employee
, и проверка на эквивалентность вернет “true”. Однако, всё ли мы сделали? Пока нет. Давайте проверим наш модифицированный класс еще одним способом.
import java.util.HashSet;
import java.util.Set;
public class EqualsTest
{
public static void main(String[] args)
{
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
//Печатает 'true'
System.out.println(e1.equals(e2));
Set employees = new HashSet();
employees.add(e1);
employees.add(e2);
//Печатает два объекта
System.out.println(employees);
}
}
Команда System.out.println(employee)
распечатывает два объекта. Если оба объекта были эквивалентны, а в Set
содержатся только уникальные объекты, то внутри HashSet
должен быть только один экземпляр, т.е. оба объекта ссылаются на одинаковые экземпляры класса Employee
. Что же мы упустили?
Мы упустили второй важный метод hashCode()
. Как сказано в документации java, если вы переопределяете метод equals()
, то вы обязаны переопределить метод hashCode()
. Итак, давайте добавим ещё один метод в наш класс Employee
.
@Override
public int hashCode()
{
final int PRIME = 31;
int result = 1;
result = PRIME * result + getId();
return result;
}
Мы добавили один раз этот метод в наш класс, и на печать будет выведен только один объект, и, таким образом, проверка эквивалентности е1 и е2 показала true.
Переопределение hashCode()
и equals()
, используя Apache Commons Lang
Apache Commons предоставляет два замечательных вспомогательных класса для вызова методов hashCode()
и equals()
. Ниже смотрим использование:
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Employee
{
private Integer id;
private String firstname;
private String lastName;
private String department;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
@Override
public int hashCode()
{
final int PRIME = 31;
return new HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME).
toHashCode();
}
@Override
public boolean equals(Object o) {
if (o == null)
return false;
if (o == this)
return true;
if (o.getClass() != getClass())
return false;
Employee e = (Employee) o;
return new EqualsBuilder().
append(getId(), e.getId()).
isEquals();
}
}
С другой стороны, если вы используете один из редакторов кода, они также должны быть способны вызывать некоторые хорошие структуры для вас. Например, если в Eclipse IDE кликнуть правой клавишей на class >> sourse > Generating hashCode() and equals() … будет сгенерирована очень хорошая реализация для вас.
То, что важно помнить.
- Всегда используйте те же атрибуты объекта для вызова и
hashCode()
иequals()
. Как раз в нашем случае, мы использовалиemployee id
. - Метод
equals()
должен быть устойчивым (если объект не изменялся, метод должен возвращать то же самое значение). - Всякий раз, когда
a.equals(b)
, тоa.hashCode()
должно быть таким же, какb.hashCode()
. - Если вы переопределили один метод, то обязательно должны переопределить второй.
Особое Внимание При Использовании ORM
Если вы имеете дело с ORM (ru.wikipedia.org/wiki/ORM), то всегда используйте геттеры и никогда не используйте вhashCode()
и equals()
ссылки на поля. Это потому, что в ORM, время от времени поля загружаются при помощи отложенной загрузки (lazy load) и не доступны, пока не вызваны их геттеры.
Например, в нашем классе Employee
, мы используем e1.id == e2.id
. Вполне возможно, что поля i
d загружены с помощью отложенной загрузки. Одно из полей может быть равно 0 или null и мы получим некорректное поведение.
Но, если используется e1.getId() == e2.getId()
, мы можем быть уверены, даже если поля были загружены с помощью отложенной загрузки; вызов геттера первым заполнит поле.
Это всё, что я знаю о методах hashCode()
и equals()
. Надеюсь, что это где-нибудь кому-нибудь поможет.
Удачи в учебе!!
p.s. Это моя первая попытка перевода. Постаралась передать всё как можно ближе к тому, что хотел сказать автор. Если есть замечания, пожалуйста, напишите в комментариях. Строго не судите :-)))
Oригинал статьи
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
З.Ы. А так статья мне понравилась)