W3docs

Metodo toString() in Java

Fai l'override di toString() nelle classi Java per ottenere rappresentazioni testuali utili nel logging e nel debugging.

toString è il metodo che System.out.println, la concatenazione di stringhe e il tuo debugger utilizzano quando devono rendere un oggetto come testo. Il comportamento predefinito — ereditato da Object — è il nome della classe più un hash esadecimale illeggibile. Fare l'override di toString è una delle cose più economiche e con il miglior rapporto costo-beneficio che puoi fare per chi leggerà i tuoi log e le tue stack trace (spesso una versione futura di te stesso).

Dove viene chiamato

Quasi mai chiami toString direttamente. Viene invocato implicitamente ogni volta che un oggetto viene convertito in testo:

Point p = new Point(3, 4);
System.out.println(p);          // calls p.toString()
String msg = "got " + p;        // calls p.toString()
log.info("point: {}", p);       // calls p.toString()

Ovunque un Object appaia in un contesto che richiede una String, toString viene eseguito. Ecco perché il comportamento predefinito è così deludente — compare ovunque e non fornisce alcuna informazione utile.

Il comportamento predefinito

Object.toString() restituisce getClass().getName() + "@" + Integer.toHexString(hashCode()). Quindi una classe semplice viene stampata come com.example.Point@1540e19d. Il nome della classe va bene; l'esadecimale è l'hash code di identità, che è inutile quando stai cercando di capire cosa contengono i campi dell'oggetto.

Un buon override

Un buon toString è breve, non ambiguo e contiene i dati che un lettore vorrebbe vedere:

@Override
public String toString() {
  return "Point[x=" + x + ", y=" + y + "]";
}

Alcune convenzioni da seguire:

  • Includi il nome della classe. Quando una riga di log mescola oggetti, sapere di che tipo si tratta è importante.
  • Usa un formato stabile e analizzabile. Class[field=value, field=value] è quello che i record e la maggior parte delle moderne librerie Java utilizzano. È leggibile e comodo per grep.
  • Mostra i campi che contano — di solito quelli presenti in equals. Tralascia campi enormi (un blob da 10MB, una Connection).
  • Non includere segreti. Password, token, PII — lasciali fuori, o mascherali. toString finisce nei file di log.

Con String.format o blocchi di testo

Per più di tre o quattro campi, String.format (o un blocco di testo) è più leggibile della concatenazione:

@Override
public String toString() {
  return String.format("User[id=%d, name=%s, role=%s]", id, name, role);
}

Non renderlo costoso

toString viene chiamato più spesso di quanto pensi — i framework di logging rendono gli argomenti in modo lazy ma abbastanza velocemente da far sì che un toString O(n²) su una lista di 100k elementi rallenterà sicuramente il tuo servizio. Mantieni l'output limitato. Il toString di una collezione dovrebbe essere limitato a una testa ragionevole, con ... (N more) per il resto.

Non lanciare eccezioni da toString

Lanciare un'eccezione in toString trasforma una normale riga di log in una NullPointerException da qualche parte inaspettata — e il problema sottostante che stavi cercando di registrare va perso. Proteggi contro i null:

@Override
public String toString() {
  return "Order[id=" + id + ", customer=" + (customer == null ? "<none>" : customer.name()) + "]";
}

I record ne ottengono uno gratuitamente

Se la tua classe è un semplice contenitore di dati, i record generano già un buon toString:

record Point(int x, int y) {}
System.out.println(new Point(3, 4));   // Point[x=3, y=4]

Quindi fare l'override di toString è principalmente qualcosa che si fa per le classi non-record.

Un esempio pratico

java— editable, runs on the server

Cosa c'è dopo

toString produce una descrizione di un oggetto. Il prossimo capitolo tratta la produzione di una copia di esso — clone, Cloneable e le scelte di design problematiche riguardo alle copie superficiali e profonde. Continua con clonazione di oggetti Java.

Esercitazione

Pratica
Quale di questi è il miglior `toString` per una classe `Point(int x, int y)`?
Quale di questi è il miglior `toString` per una classe `Point(int x, int y)`?
Was this page helpful?