Jak uzyskać „odwołanie do obiektu” obiektu w Javie, gdy toString () i hashCode () zostały nadpisane?

106

Chciałbym wydrukować "odniesienie do obiektu" obiektu w Javie dla celów debugowania. To znaczy, aby upewnić się, że przedmiot jest taki sam (lub inny) w zależności od sytuacji.

Problem polega na tym, że omawiana klasa dziedziczy z innej klasy, która nadpisała zarówno toString (), jak i hashCode (), co zwykle daje mi identyfikator.

Przykładowa sytuacja: Uruchomienie aplikacji wielowątkowej, w której chcę (podczas programowania) sprawdzić, czy wszystkie wątki używają tego samego wystąpienia obiektu zasobu, czy nie.

Nicolai
źródło
1
w zależności od tego, czy w ogóle możesz to zrobić ... == jest do zrobienia ... ale nie mam pojęcia, jak skonstruowany jest dany kod. Ponownie hashCode jest prawdopodobnie odpowiedni do tego, co robisz, ale może się zepsuć w zależności od tego, jak zaimplementowano bibliotekę.
TofuBeer
To naprawdę dobre pytanie.
Zhang Xiang

Odpowiedzi:

108

Co dokładnie planujesz z tym zrobić (to, co chcesz zrobić, ma znaczenie dla tego, do czego będziesz musiał zadzwonić).

hashCode, zgodnie z definicją w JavaDocs, mówi:

O ile jest to rozsądnie praktyczne, metoda hashCode zdefiniowana przez klasę Object zwraca różne liczby całkowite dla różnych obiektów. (Zazwyczaj jest to realizowane poprzez konwersję adresu wewnętrznego obiektu na liczbę całkowitą, ale ta technika implementacji nie jest wymagana przez język programowania Java ™).

Więc jeśli używasz, hashCode()aby dowiedzieć się, czy jest to unikalny obiekt w pamięci, nie jest to dobry sposób, aby to zrobić.

System.identityHashCode wykonuje następujące czynności:

Zwraca ten sam kod skrótu dla danego obiektu, jaki byłby zwracany przez domyślną metodę hashCode (), niezależnie od tego, czy klasa danego obiektu przesłania hashCode (). Kod skrótu dla odwołania zerowego wynosi zero.

Co, z powodu tego, co robisz, brzmi jak to, czego chcesz ... ale to, co chcesz zrobić, może nie być bezpieczne, w zależności od sposobu implementacji biblioteki.

TofuBeer
źródło
6
Nie działam na wartości w kodzie. Jak ks. edytuj moje pytanie, używam go tylko do debugowania w określonej sytuacji. Dlatego uważam, że moja odpowiedź jest rozsądna, ale daję ci +1 za wnikliwą odpowiedź.
Nicolai
1
jest szansa, że ​​zawsze będzie robić to, co chcesz - ale może się zepsuć na niektórych maszynach wirtualnych.
TofuBeer
Zepsuje się (tj. IdentityHashCode niekoniecznie będzie unikalny) na dowolnej rozsądnej maszynie wirtualnej. IdentyfikacjaHashCode nie jest identyfikatorem
Tom Hawtin - tackline
Jak już wspomniano, nie ma gwarancji, że kod skrótu będzie oparty na adresie. Widziałem wiele obiektów o tym samym identyfikatorze występujących na maszynie wirtualnej IBM w WAS.
Robin
„Zazwyczaj jest to realizowane poprzez konwersję adresu wewnętrznego obiektu na liczbę całkowitą”, a nie gwarancja, ale domyślna implementacja firmy Sun. Takie rzeczy jak s = "Hello" i t = "Hello" prawdopodobnie spowodowałyby, że s i t miałyby ten sam kod IdentityHashCode, ponieważ w rzeczywistości są tym samym obiektem.
TofuBeer
50

Oto jak to rozwiązałem:

Integer.toHexString(System.identityHashCode(object));
Nicolai
źródło
5
W rzeczywistości nie jest to poprawne, ponieważ wiele obiektów może zwracać ten sam kod identityHashCode.
Robin
2
Czy nie jest prawdą, że dwa obiekty (odniesienia) z tym samym hashem tożsamości są tym samym obiektem? tego chce OP
basszero,
3
Nie, to nieprawda. Jest to bardzo prawdopodobne, ale nie jest to gwarantowane, ponieważ specyfikacja NIE definiuje algorytmu.
Robin
8

Double equals ==zawsze sprawdza na podstawie tożsamości obiektu, niezależnie od implementacji obiektu hashCode lub equals. Oczywiście - upewnij się, że porównujesz odniesienia do obiektów volatile(w JVM 1.5+).

Jeśli naprawdę musisz mieć oryginalny wynik Object toString (chociaż nie jest to najlepsze rozwiązanie dla twojego przykładowego przypadku użycia), biblioteka Commons Lang ma metodę ObjectUtils.identityToString (Object) , która zrobi to, co chcesz. Z JavaDoc:

public static java.lang.String identityToString(java.lang.Object object)

Pobiera toString, który zostałby utworzony przez Object, gdyby klasa nie przesłaniała samej metody toString. null zwróci wartość null.

 ObjectUtils.identityToString(null)         = null
 ObjectUtils.identityToString("")           = "java.lang.String@1e23"
 ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"
noahlz
źródło
1
Jeśli używasz Java 7, powinieneś rozważyć użycie java.util.Objects
noahlz
5

Nie możesz bezpiecznie robić tego, co chcesz, ponieważ domyślny hashCode () może nie zwrócić adresu, a jak już wspomniano, możliwych jest wiele obiektów z tym samym hashCode. Jedynym sposobem osiągnięcia tego, co chcesz, jest faktycznie przesłonięcie metody hashCode () dla danych obiektów i zagwarantowanie, że wszystkie one dostarczają unikalnych wartości. To inna kwestia, czy jest to wykonalne w Twojej sytuacji.

Dla przypomnienia, doświadczyłem wielu obiektów z tym samym domyślnym hashcode na maszynie wirtualnej IBM działającej na serwerze WAS. Mieliśmy błąd polegający na tym, że obiekty umieszczane w zdalnej pamięci podręcznej były przez to nadpisywane. To było dla mnie otwarciem oka w tamtym momencie, ponieważ założyłem, że domyślnym hashcode był również adres pamięci obiektów.

Rudzik
źródło
2

Dodaj unikalny identyfikator do wszystkich swoich instancji, tj

public interface Idable {
  int id();
}

public class IdGenerator {
  private static int id = 0;
  public static synchronized int generate() { return id++; }
}

public abstract class AbstractSomething implements Idable {
  private int id;
  public AbstractSomething () {
    this.id = IdGenerator.generate();
  }
  public int id() { return id; }
}

Rozszerz z AbstractSomething i wykonaj zapytanie o tę właściwość. Będzie bezpieczny w pojedynczej maszynie wirtualnej (zakładając, że nie ma gry z programami ładującymi klasy w celu obejścia statystyk).

Michael Myers
źródło
Prawdopodobnie użyłbym AtomicInteger w tym scenariuszu - ma wyższą przepustowość, ponieważ synchronizacja nie jest wymagana i wykorzystuje natywne operacje na pamięci atomowej dostarczone przezsun.misc.Unsafe
RAnders00
1

możemy po prostu skopiować kod z tostring klasy obiektu, aby uzyskać odniesienie do łańcucha

class Test
{
  public static void main(String args[])
  {
    String a="nikhil";     // it stores in String constant pool
    String s=new String("nikhil");    //with new stores in heap
    System.out.println(Integer.toHexString(System.identityHashCode(a)));
    System.out.println(Integer.toHexString(System.identityHashCode(s)));
  }
}
Nikhil Jaitak
źródło