Как мы знаем, класс Object является родительским классом для всех классов, которые вы когда-либо создадите, и для всех классов, которые уже существуют в языке Java. Класс объекта имеет несколько методов, которые доступны всем его подклассам. Можете ли вы назвать все его методы? Давай, попробуй найти, если это не в твоей голове прямо сейчас. Я расскажу о двух из его методов, которые широко используются, а именно.

  1. равно(объект): логическое значение
  2. hashCode(): целое

Первый метод — equals(Object), который используется для сравнения двух объектов, если они равны или нет. Итак, давайте посмотрим, что делает метод equals класса Object.

public boolean equals(Object obj) {
 return this == obj;
}

В основном это делает ссылочное равенство, а не логическое равенство, и возвращает true или false на основе этого. Так что в основном, если это две книги с одинаковым названием

Book book1 =new Book( “Java” );
Book book2 =new Book( “Java” );

Итак, book1.equals(book2) вернет false, если вы не переопределите equals() в своем классе, так как он будет сравнивать адреса памяти обеих книг в equals() объекта. class, и оба имеют разные адреса, хотя оба одинаковы. Так какой же смысл использовать equals() в классе Object, если он не может делать эту мелочь? Думать!

Причина в том, что equals() Object не знает о вашем классе Book, и самое меньшее, что он может сделать, это сравнить расположение памяти обоих объектов и сообщить вам, указывают ли они на один и тот же адрес памяти. Для логического сравнения ваших объектов вам необходимо переопределить equals() в вашем классе и обеспечить правильную реализацию для всех полей, по которым вы хотите сравнить объекты. Большинство IDE генерируют автоматическую реализацию equals() для вашего класса. Давайте посмотрим, как Intellij IDEA создаст equals() для следующего класса Book.

public class Book {
 String title;
 String author;
 BookType bookType;
}

И равны ()…

@Override
public boolean equals(Object o) {
 1 if (this == o) return true; 
 2 if (o == null || getClass() != o.getClass()) return false; 
 3 Book book = (Book) o; 
 4 return Objects.equals( title, book.title ) && 
 5 Objects.equals( author, book.author ) && 
 6 bookType == book.bookType; 
}

Вышеупомянутый метод equals(), сгенерированный IntelliJ, выглядит интересно, и, глядя на него, вы можете догадаться, что IntelliJ действительно умен. Пусть отлаживает его построчно.

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

Строка 2 проверяет, является ли параметризованный объект нулевым или нет, далее она проверяет, принадлежат ли оба объекта this и o к одному и тому же классу, поскольку нет смысла сравнивать два разных объекты.

Не доходит до последней строчки? Хорошо! Будете ли вы сравнивать объект Book с объектом Car, чтобы увидеть, равны они или нет? Конечно нет!

Строка 3 просто приводит параметризованный объект к Book, поскольку он переопределяет сравнение объектов класса Book.

Следующая строка 4 и строка 5 сравнивают поля заголовка и автора обоих объектов, используя equals() класса Objects, который является нулевым и использует equals() первого параметра для проверки равенства.

В строке 6 мы сравниваем два перечисления, и в случае перечислений мы проверяем ссылки, поскольку переменные, которые содержат одно и то же значение перечисления, указывают на один и тот же адрес памяти.

Итак, мы видим, насколько умно IntelliJ реализует equals(), поэтому, если вы хотите написать equals без использования IDE, позаботьтесь о вышеперечисленных вещах.

Теперь поговорим о hashCode() класса Object.

Это еще один очень важный метод наряду с equals() в классе Object, который возвращает целочисленное представление адреса памяти объекта. Это целое число уникально для каждого экземпляра, и нет необходимости каждый раз возвращать одно и то же целое число для одного и того же объекта. Однако документация по Java рекомендует переопределять hashCode() в каждом классе, который переопределяет equals(). Но почему? Давайте посмотрим. Существует некоторый контракт, связанный с hashCode(), который выглядит следующим образом:

  • Когда метод hashCode повторно вызывается для объекта во время выполнения приложения, он должен постоянно возвращать одно и то же значение при условии, что никакая информация, используемая в сравнениях на равенство, не изменяется.
  • Если два объекта равны согласно методу equals(Object), то вызов hashCode для двух объектов должен давать один и тот же целочисленный результат.
  • Если два объекта не равны в соответствии с методом equals(Object), не требуется, чтобы вызов hashCode для каждого из объектов давал различные результаты.

Если вы внимательно прочитаете второй контракт выше, в нем говорится, что два объекта, которые равны в соответствии с equals(), тогда оба должны иметь одно и то же значение, возвращаемое hashCode(). Поэтому, если вы не переопределите его, вы получите отличное значение hasCode для одинаковых одинаковых объектов.

Другая причина, по которой java рекомендует, заключается в том, что если мы не переопределим ее, такие коллекции, как HashSet и HashMap, не будут работать должным образом, поскольку для работы с ними требуется значение hashCode объектов. Вот как будет выглядеть переопределенный hashCode() класса Book выше:

@Override
public int hashCode() {
 return Objects.hash( title, author, bookType );
}

Это обеспечит соблюдение генерального контракта, упомянутого выше.