Почему в Object были определены equals и hashCode? - PullRequest
15 голосов
/ 13 ноября 2011

С чем связано решение включить эти методы в java.lang.Object? Равенство и хеширование не имеют смысла для многих классов.

Логичнее было бы сделать два интерфейса:

interface Equalable {
    boolean equals(Equalable other);
}

interface Hashable extends Equalable {
    int hashCode();
}

Например, определение HashSet может выглядеть как

class HashSet<T extends Hashable> ...

Это предотвратит одну из распространенных ошибок новичка - использование набора элементов без реализации equals / hashCode.

Ответы [ 9 ]

14 голосов
/ 13 ноября 2011

Когда мы реализуем Interface, мы inject (or accept) заключаем контракт, определенный интерфейсом.

Equalable & Hashable - это два разных договора.Но если мы внимательно посмотрим, то увидим, что оба они зависят друг от друга, что означает, что они являются частью single interface, что-то вроде EqualableAndHashable.

Теперь очевидный вопрос:они должны быть частью этого нового EqualableAndHashable интерфейса или Object?

Давайте выясним.У нас есть == (equal operator) для проверки равенства двух объектов.Оператор == подтверждает, равны ли значения / ссылки для двух разных примитивов / объектов.Но на это не всегда возможно ответить, просто проверив оператор ==.

Теперь вопрос заключается в том, должно ли это равенство which is also a contract вводиться через интерфейсы или часть класса Object?

Если мы посмотрим, мы не можем просто сказать что-то вроде:

TypeX не гарантирует контракт на равенство.

Он станетхаос, если некоторые типы объектов предлагают равенство, а некоторые нет.Это означает, что объект TypeX должен соблюдать контракт на равенство, который также справедлив для всех других типов объектов.Таким образом, он не должен вводить равенство из интерфейса, потому что равенство должно быть частью контракта для любого объекта по умолчанию, в противном случае это создаст хаос.

Поэтому нам нужно, чтобы Объекты пришли к реализации equals.Но он не может реализовать только метод equals, ему также нужно реализовать метод hashcode.

2 голосов
/ 13 ноября 2011

Реализация по умолчанию в java.lang.Object имеет смысл.Часто это достаточно хорошо.В JPA / веб-приложениях я нахожу себя очень редко, если когда-либо переопределяю equals и hashCode.

Лучший вопрос может быть: для неизменяемых значений объектов, таких как String, Long и т. Д., Почему вы не можете переопределить ==Оператор для вызова равно (), как вы можете в C #?Я видел гораздо больше ошибок из-за того, что по умолчанию equals / hashCode не работает правильно.Например,

Long x = obj.getId(); 
Long y = obj2.getId();  
if (x == y) { // oops, probably meant x.equals(y)! }

Это справедливый вопрос, однако, почему методы по умолчанию не заблокированы за интерфейсом тегирования, как по умолчанию Object.clone ().Существует реализация по умолчанию, но вы должны явно подтвердить, что хотите использовать ее, внедрив Cloneable.С таким же успехом мог бы существовать подобный интерфейс тегирования, такой как Collectible или Equatable, и тогда сигнатура для методов коллекций могла бы быть Equatable вместо Object.

1 голос
/ 13 ноября 2011

Изначально в Java не было дженериков. Это было обойдено, позволяя любому Object быть членом любой коллекции, и, таким образом, любые Object должны hashCode и equals. К настоящему времени он слишком укоренился, чтобы измениться.

1 голос
/ 13 ноября 2011

Мхх, не уверен, но к моменту выхода Java 1.0 дженерики еще не существовали. Они были добавлены в Java 5.0 в 2004 году .. поэтому ваше предложение не может быть реализовано для Java 1.0

1 голос
/ 13 ноября 2011

(Лично, если бы они были в интерфейсе, я бы поместил их туда, чтобы избежать хотя бы одного класса equals / hashCode ошибок.)

Я думаю, вы бы хотелиреализация в Object, как запасной механизм, означающий, что все будет иметь реализацию в любом случае, интерфейс или нет.

Я подозреваю, что многое из этого является историческим;программирование Java сегодня выглядит несколько иначе, чем программирование Java тогда.

0 голосов
/ 25 сентября 2012

Любой объект, независимо от его типа, может разумно ответить, эквивалентен ли он любому другому объекту, даже если тип другого объекта тот, о котором он никогда не слышал. Если он никогда не слышал о типе другого объекта, одного этого факта достаточно, чтобы сообщить, что он не эквивалентен последнему объекту.

0 голосов
/ 14 ноября 2011

Действительно, это просто для удобства, и так лучше.Хорошо, подумайте о том, что потребуется для обеспечения равенства объектов, если у вас не было метода .equals:

AreEqual(Object obj1,Object obj2) {
  if(!obj1 instanceof Equalable) return false;
  if(!obj2 instanceof Equalable) return false;
  return ((Equalable)(obj1).equals((Equalable)obj2);
}

Это верно даже для hashCode.Иногда ссылочного равенства достаточно.Если вы заставили HashSet принимать только объекты, которые реализуют Hashable, вам бы пришлось явно сделать ваши классы Hashable, даже если вы хотите только ссылочное равенство.И вы уменьшили универсальность отличной структуры данных.

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

0 голосов
/ 13 ноября 2011

Если у вас есть список объектов и вы вызываете метод contains, что должен сделать Java?Я думаю, что реализация по умолчанию (сравнение ссылок) является достойным решением.Таким образом, вам не придется реализовывать себя equals и hashcode для каждого класса, который вы используете в коллекции.

0 голосов
/ 13 ноября 2011

Это общая реализация. Вы должны переопределить реализацию, если они вам нужны. В противном случае у вас есть разумная реализация по умолчанию.

По крайней мере, равные должны быть. Создание интерфейсов для основных операций, вероятно, потребовало бы значительных накладных расходов.

...