W3docs

Java toString()-Methode

toString() in Java-Klassen überschreiben, um nützliche Zeichenkettendarstellungen für Logging und Debugging zu erzeugen.

toString ist die Methode, die System.out.println, die String-Verkettung und der Debugger aufrufen, wenn sie ein Objekt als Text darstellen müssen. Die Standardimplementierung — geerbt von Object — besteht aus dem Klassennamen plus einem unlesbaren Hex-Hash. toString zu überschreiben ist eine der günstigsten Maßnahmen mit dem höchsten Nutzen, die man für alle tun kann, die später Logs und Stack-Traces lesen müssen (häufig eine zukünftige Version von einem selbst).

Wo es aufgerufen wird

toString wird fast nie direkt aufgerufen. Es wird implizit aufgerufen, sobald ein Objekt in Text umgewandelt wird:

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()

Überall dort, wo ein Object in einem Kontext erscheint, der einen String erwartet, wird toString ausgeführt. Deshalb ist die Standardimplementierung so enttäuschend — sie taucht überall auf und vermittelt keinerlei nützliche Informationen.

Die Standardimplementierung

Object.toString() gibt getClass().getName() + "@" + Integer.toHexString(hashCode()) zurück. Eine einfache Klasse wird also als etwas wie com.example.Point@1540e19d ausgegeben. Der Klassenname ist in Ordnung; der Hex-Wert ist der Identity-Hash-Code, der nutzlos ist, wenn man herausfinden möchte, welche Felder das Objekt enthält.

Eine gute Überschreibung

Ein gutes toString ist kurz, eindeutig und enthält die Daten, die ein Leser sehen möchte:

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

Ein paar Konventionen, die es wert sind, befolgt zu werden:

  • Den Klassennamen einschließen. Wenn eine Log-Zeile mehrere Objekte mischt, ist es wichtig zu wissen, um welchen Typ es sich handelt.
  • Eine stabile, parsierbare Form verwenden. Class[field=value, field=value] ist das Format, das Records und die meisten modernen Java-Bibliotheken verwenden. Es ist lesbar und grep-freundlich.
  • Die relevanten Felder anzeigen — in der Regel dieselben wie in equals. Riesige Felder weglassen (ein 10-MB-Blob, eine Connection).
  • Keine Geheimnisse einschließen. Passwörter, Tokens, personenbezogene Daten — weglassen oder maskieren. toString landet in Log-Dateien.

Mit String.format oder Textblöcken

Für mehr als drei oder vier Felder ist String.format (oder ein Textblock) lesbarer als Verkettung:

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

Nicht zu aufwändig machen

toString wird häufiger aufgerufen, als man denkt — Log-Frameworks rendern Argumente zwar lazy, aber oft genug, dass ein O(n²)-toString auf einer 100k-Elemente-Liste den Dienst definitiv verlangsamen wird. Die Ausgabe sollte begrenzt bleiben. Das toString einer Collection sollte auf einen sinnvollen Kopf beschränkt sein, mit ... (N more) für den Rest.

Keine Ausnahmen in toString werfen

Eine Ausnahme in toString zu werfen, verwandelt eine gewöhnliche Log-Zeile in eine NullPointerException an unerwarteter Stelle — und das eigentliche Problem, das man zu loggen versuchte, geht verloren. Gegen null-Werte absichern:

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

Records erhalten es kostenlos

Wenn eine Klasse ein reiner Datenträger ist, generieren Records bereits ein gutes toString:

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

Das Überschreiben von toString ist daher hauptsächlich für Nicht-Record-Klassen erforderlich.

Ein ausgearbeitetes Beispiel

java— editable, runs on the server

Was kommt als Nächstes

toString erzeugt eine Beschreibung eines Objekts. Das nächste Kapitel behandelt die Erstellung einer Kopie davon — clone, Cloneable und die schwierigen Designentscheidungen rund um flache vs. tiefe Kopien. Weiter zu Java-Objektklonierung.

Übungen

Übung
Welches ist das beste `toString` für eine `Point(int x, int y)`-Klasse?
Welches ist das beste `toString` für eine `Point(int x, int y)`-Klasse?
Was this page helpful?