Java hashcode, основанный на идентичности

Поведение Object.hashCode() по умолчанию заключается в том, чтобы вернуть по существу "адрес" объекта, чтобы a.hashCode() == b.hashCode() тогда и только тогда, когда a == b. Как я могу получить это поведение в пользовательском классе, если суперкласс уже определяет hashCode()? Например:

class A {
  public int hashCode() {
    return 0;
  }
}

class B extends A {
  public int hashCode() {
    // Now I want to return a unique hashcode for each object.
    // In pythonic terms, it'd look something like:
    return Object.hashCode(this);
  }
}

Идеи?

Ответ 1

System.identityHashCode(Object) обеспечивает такое поведение.

Вы бы это сделали:

class B extends A {
  public int hashCode() {
    return System.identityHashCode(this);
  }
}

Пожалуйста, проверьте метод equals, чтобы он возвращал true, если оба объекта одинаковы. В противном случае это нарушит поведение, описанное для equals и hashCode. (Чтобы быть верным, метод equals должен возвращать false, если вы получаете разные хэш-коды для двух объектов.) Чтобы обеспечить реализацию equals(), которые соответствуют заданному методу hashCode():

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

Ответ 2

Используйте System.identityHashCode(). Это то, что использует IdentityHashMap.

Вы должны быть крайне опасаться переопределения существующего hashCode() с этим, хотя, поскольку вы можете разорвать контракт hashCode, будучи двумя объектами, которые:

если a.equals(b), то a.hashCode() должен быть равен b.hashCode()

Вы можете сломать это, переопределив существующее поведение, или вам может потребоваться переопределить equals() тоже.

Ответ 3

Как сказал Мнемент, все, я хотел бы указать, что hashCode(), возвращающий 0 (или любое постоянное значение), является действительным (хотя и хромым). hashCode() может (и должен) возвращать разные значения для a и b, только если! a.equals(b).
Так, например, у вас есть

class A {
  public int hashCode() {
    return 0;
  }
  public boolean equals(Object o) {
    return o instanceof A; // all objects are equal
  }
}

class B extends A {
  public int hashCode() {
    return System.identityHashCode(this);
  }
  public boolean equals(Object o) {
    return this.hashCode().equals(o.hashCode());
  }
}

Теперь вы создаете два объекта:

A a = new A();
A b = new B();

И вдруг a.equals(b), но! b.equals(a). Конечно, в более реальной жизни equals() в будет более сложным, но проблема все еще сохраняется. Чтобы избавиться от этой проблемы, вы всегда хотите вызвать

if (super.equals(o)) return true;

в начале новых equals().

И так как переопределение hashCode() строго связано с переопределением equals(), вы хотите убедиться, что везде super.equals() вернётся true для любых двух заданных объектов, новый hashCode() вернет super.hashCode().