W3docs

La classe Object in Java

Metodi ereditati da java.lang.Object in Java — toString, equals, hashCode, getClass, clone e finalize.

Ogni classe in Java — che tu scriva extends Object o meno — si trova sotto java.lang.Object nella gerarchia dei tipi. Ciò significa che ogni riferimento che detieni ha a disposizione un piccolo insieme di metodi: toString, equals, hashCode, getClass e alcuni altri. Capire cosa fanno per impostazione predefinita, e quando dovresti sovrascriverli, è la differenza tra oggetti che funzionano bene con le collection, i debugger e i framework — e oggetti che non lo fanno.

Questo capitolo è la mappa del territorio. I prossimi capitoli approfondiscono i metodi specifici (equals/hashCode, toString, clone) uno alla volta.

Estensione implicita

Scrivi una classe senza la clausola extends:

public class Box {
  int contents;
}

Il compilatore la tratta come se avessi scritto public class Box extends Object. Ecco perché puoi passare un Box a qualsiasi cosa dichiarata come Object — ogni tipo riferimento è in definitiva un Object.

I metodi ereditati a colpo d'occhio

MetodoCosa fa il comportamento predefinito
toString()Restituisce ClassName@hashCodeHex — quasi mai quello che vuoi
equals(Object)Uguaglianza per riferimento (==)
hashCode()Un valore derivato dall'identità dell'oggetto, non dal suo contenuto
getClass()Il token Class<?> a runtime — mai null
clone()Una copia superficiale campo per campo, ma solo sulle classi che implementano Cloneable; altrimenti lancia un'eccezione
wait(), notify(), notifyAll()Primitive monitor di basso livello usate con synchronized
finalize()Hook chiamato dal GC prima di recuperare un oggetto — deprecato, non usare

I primi tre — toString, equals e hashCode — sono quelli che quasi ogni classe che porta dati dovrebbe sovrascrivere, perché le versioni ereditate descrivono l'identità di un oggetto anziché il suo contenuto. getClass lo chiami sugli oggetti ma non lo sovrascrivi mai. Le primitive per i thread le tocchi raramente direttamente. clone e finalize è meglio evitarli nel codice moderno (clone ha un capitolo dedicato; finalize è sostituito da try-with-resources e Cleaner).

Nota
Se usi un record (Java 16+), il compilatore genera toString, equals e hashCode per te a partire dai componenti — tutti e tre basati sul contenuto, non sull'identità. Questo elimina la maggior parte del codice boilerplate di cui parla questo capitolo. Vedi Java Records.

toString — il comportamento predefinito non è utile

Il toString ereditato restituisce qualcosa come Box@1540e19d. Si tratta del nome della classe e dell'hash code identitario in esadecimale — inutile per il logging, il debugging o qualsiasi tipo di diagnostica:

Box b = new Box();
System.out.println(b);   // Box@1540e19d

Sovrascrivilo per restituire qualcosa di leggibile — println, la concatenazione di stringhe, i framework di logging e i debugger degli IDE chiamano tutti toString per te, quindi una buona implementazione ripaga ovunque:

@Override
public String toString() {
  return "Box[contents=" + contents + "]";
}

Il prossimo capitolo mostra come farlo bene.

equals e hashCode — vanno insieme

Il metodo equals ereditato restituisce true solo quando entrambi i riferimenti puntano allo stesso oggetto:

String a = new String("hi");
String b = new String("hi");
System.out.println(a == b);       // false — different objects
System.out.println(a.equals(b));  // true — String overrides equals

String sovrascrive equals per confrontare il contenuto. Una classe che scrivi tu non lo fa, a meno che tu non lo sovrascriva. E nel momento in cui sovrascrivi equals, devi sovrascrivere anche hashCode — altrimenti i tuoi oggetti si comporteranno in modo silenziosamente scorretto in HashSet e HashMap. Il capitolo su equals e hashCode copre le regole esatte.

Attenzione
Sovrascrivi equals senza sovrascrivere hashCode e due oggetti che consideri uguali possono finire in bucket hash diversi — così un HashSet memorizza entrambi senza problemi, e map.get(key) restituisce null per una chiave che è chiaramente presente. Sovrascrivi sempre i due insieme e mantienili coerenti: gli oggetti uguali devono restituire hash code uguali.

getClass — il token del tipo a runtime

getClass() restituisce l'oggetto Class<?> che descrive il tipo a runtime:

Object o = "hello";
System.out.println(o.getClass().getName());  // java.lang.String

Viene usato nel codice riflessivo, dalle implementazioni di equals che vogliono confronti di tipo esatto, e dall'output di debugging. Nota che getClass() è final — non puoi sovrascriverlo.

clone e Cloneable

Object.clone() è protected e ha successo solo sulle classi che dichiarano esplicitamente implements Cloneable. Il risultato è una copia superficiale: una nuova istanza con gli stessi valori di campo, inclusi gli stessi riferimenti agli oggetti secondari mutabili. È un'API notoriamente scomoda, trattata in dettaglio nel capitolo sulla clonazione.

wait / notify

Questi metodi, insieme a notifyAll, fanno parte delle primitive di concorrenza di basso livello originali di Java. Sono legati ai blocchi synchronized e vengono usati per costruire pattern di attesa-per-condizione. Nel codice moderno, preferisci le utilità di livello più alto in java.util.concurrent (lock, BlockingQueue, CompletableFuture); i wait/notify grezzi sono rari al di fuori degli internals delle librerie.

finalize — lascialo perdere

finalize() era il modo del GC di dare al tuo oggetto un'ultima possibilità di rilasciare risorse. È stato deprecato da Java 9 e rimosso nelle versioni più recenti. Usa try-with-resources per I/O e java.lang.ref.Cleaner per il caso molto raro in cui devi eseguire codice dopo la raccolta.

Un esempio pratico

java— editable, runs on the server

Cosa viene dopo

La sovrascrittura più comune — e più soggetta a errori — di un metodo Object è equals abbinato a hashCode. Il prossimo capitolo illustra il contratto che entrambi i metodi devono rispettare e i pattern per farlo correttamente. Continua con Java equals e hashCode.

Esercizi

Pratica
Cosa restituisce il metodo `toString()` predefinito ereditato da `Object`?
Cosa restituisce il metodo `toString()` predefinito ereditato da `Object`?
Was this page helpful?