Ist es wirklich sinnvoll, toString () für Entity-Klassen zu implementieren?

8

Es wird empfohlen, die Methode toString() einer Klasse zu überschreiben (implementieren).

  • Die Java-API-Dokumentation selbst sagt "Es wird empfohlen, dass alle Unterklassen diese Methode überschreiben.".
  • Bloch, in Effektives Java hat den Punkt "ToString immer überschreiben". Und nur ein Idiot widerspricht Bloch, richtig?

Ich zweifle jedoch an diesem Ratschlag: Lohnt es sich wirklich, toString() für Entitätsklassen zu implementieren?

Ich werde versuchen, meine Argumentation auszulegen.

  1. Ein -Entität -Objekt hat eine eindeutige Identität; Es ist nie dasselbe wie ein anderes Objekt, selbst wenn die beiden Entitäten äquivalente Attributwerte haben. Das heißt, (für Nicht-Null x ) gilt die folgende Invariante für eine Entitätsklasse (per Definition):

    x.equals(y) == (x == y)

  2. Die Methode toString() gibt eine Zeichenfolge zurück, die ihr Objekt "textuell" darstellt (in den Worten der Java-API).

  3. Eine gute Repräsentation erfasst das Wesentliche des Objekts. Wenn also zwei Darstellungen unterschiedlich sind, sind sie Repräsentationen verschiedener (nicht-äquivalenter) Objekte und umgekehrt, wenn zwei Repräsentationen äquivalent sind, sind sie äquivalente Repräsentationen Objekte. Dies legt die folgende Invariante für eine gute Repräsentation nahe (für Nicht-Null x , y ):

    x.toString().equals(y.toString()) == x.equals(y)

  4. Also für Entitäten, die wir erwarten %Code% Das heißt, jedes Entitätsobjekt sollte eine eindeutige textuelle Repräsentation haben, die x.toString().equals(y.toString()) == (x == y) zurückgibt. Einige Entitätsklassen haben einen eindeutigen Namen oder ein numerisches ID-Feld, sodass ihre toString() -Methode eine Repräsentation zurückgeben könnte, die diesen Namen oder diese numerische ID enthält. Aber im Allgemeinen hat die Methode toString() keinen Zugriff auf ein solches Feld.

  5. Ohne ein eindeutiges Feld für eine Entität ist toString() am besten in der Lage, ein Feld einzubeziehen, das für verschiedene Objekte wahrscheinlich nicht identisch ist. Aber das ist genau die Anforderung von toString() , was System.identityHashCode() bietet.

  6. Also Object.toString() ist OK für ein Entitätsobjekt, das keine Datenelemente hat, aber für die meisten Klassen möchten Sie sie in die Textdarstellung aufnehmen, richtig? In der Tat möchten Sie alle davon einschließen: Wenn der Typ ein (nicht null) Datenelement x aufweist, möchten Sie Object.toString() in das Feld einschließen Darstellung.

  7. Dies verursacht jedoch ein Problem für Datenelemente, die Verweise auf andere Entitäten enthalten: das sind Verknüpfungen . Wenn ein Objekt x.toString() ein Datenelement Person hat, erzeugt die naive Implementierung ein Fragment des Familienstammbaums dieser Person, nicht von Person father selbst. Wenn es Zweiwege-Assoziationen gibt, wird eine naive Implementierung rekursiv behandelt, bis Sie einen Stack-Überlauf bekommen . Vielleicht überspringen Sie also die Daten, die sie halten Assoziationen?

  8. Aber was ist mit einem Werttyp Person mit Marriage und Person husband Datenelementen? Diese Assoziationen sollten von Person wife gemeldet werden. Die einfachste Methode, alle Marriage.toString() -Methoden zu verwenden, besteht darin, dass toString() nur die Identitätsfelder ( Person.toString() oder Person.name ) von System.identityhashCode(this) meldet.

  9. Es scheint also, dass die zur Verfügung gestellte Implementierung von Person für Entity-Klassen eigentlich nicht zu schlecht ist. In diesem Fall, warum überschreiben Sie es?

Um es konkret zu machen, betrachten Sie den folgenden Code:

%Vor%

Wie nützlich wäre eine Überschreibung von toString() beim Debuggen eines toString() , das von IlegalArgumentException geworfen wurde?

    
Raedwald 03.02.2011, 18:28
quelle

5 Antworten

8

Punkt # 3 ist das schwache Glied in diesem Argument, und ich stimme tatsächlich gewaltsam damit nicht überein. Ihre Invariante ist (nachbestellt)

%Vor%

Ich würde eher sagen:

%Vor%

Das heißt, logische Implikation. Wenn x und y gleich sind, sollten ihre toString () s gleich sein, aber ein gleich toString () bedeutet nicht bedeutet notwendigerweise, dass die Objekte gleich sind (denken Sie an die Beziehung equals() : hashCode() Gleiche Objekte müssen denselben Hash-Code haben, aber derselbe Hash-Code kann nicht sein, um zu bedeuten, dass die Objekte gleich sind.

Grundsätzlich hat toString() im programmatischen Sinne keine Bedeutung, und ich denke, du versuchst, es mit einem zu versehen. toString() ist am nützlichsten als Werkzeug zum Protokollieren usw .; Sie fragen, wie nützlich eine überschriebene toString() wäre:

%Vor%

Ich würde sagen, es ist massiv nützlich. Angenommen, Sie bemerken viele Fehler in Ihren Protokollen und sehen:

%Vor%

Was machst du? Du hast überhaupt keine Ahnung was vor sich geht. Wenn Sie sehen:

%Vor%

Dann haben Sie tatsächlich eine Chance, herauszufinden, was vor sich geht ("Hey, jemand versucht, die Familie Smith dazu zu bringen, sich zu heiraten"), und das hilft vielleicht beim Debuggen usw. Java-Objekt-IDs geben Ihnen keine Informationen überhaupt .

    
Cowan 03.02.2011, 22:00
quelle
10
  

So scheint es, dass die zur Verfügung gestellte Implementierung von toString () eigentlich nicht zu schlecht für Entity-Klassen ist. In diesem Fall, warum überschreiben Sie es?

Was lässt Sie denken, dass das Ziel von toString() einfach darin besteht, einen eindeutigen String zu haben? Das ist nicht sein Zweck. Sein Zweck ist es, Ihnen einen Kontext über die Instanz zu geben, und einfach geben ein Klassenname und ein Hashcode keinen Kontext.

Bearbeiten

Ich möchte nur sagen, dass ich keinesfalls denke, dass Sie toString() auf jedes -Objekt überschreiben müssen. Wertlose Objekte (wie eine konkrete Implementierung eines Listeners oder einer Strategie) müssen toString() nicht überschreiben, da jede einzelne Instanz von keiner anderen unterscheidbar ist, dh der Klassenname ist ausreichend.

    
Mark Peters 03.02.2011 18:35
quelle
3

Die Verwendung einer toString() -Methode in Entitätsklassen kann für das Debugging enorm hilfreich sein. Aus praktischer Sicht vereinfacht die Verwendung von IDE-Vorlagen oder etwas wie dem Projekt Lombok @ToString Annotation dies sehr und macht es einfach schnell implementieren.

    
Kai Sternad 03.02.2011 18:39
quelle
2

Ich benutze toString () immer für meine eigenen Zwecke und nicht wegen einer technischen Anforderung. Wenn ich eine Person-Klasse habe, gibt die toString-Methode den Namen der Person zurück. Nicht mehr und nicht weniger. Es ist nicht einzigartig, aber zu Debugging-Zwecken genügt es zu sehen, welche Person gemeint ist. Besonders in der Web-Entwicklung ist dies sehr nützlich, wenn ich nur den Objektnamen in JSPs schreiben muss, um den Namen der Person zu erhalten, damit ich weiß, dass ich das richtige Objekt habe.

Wenn das Objekt einige eindeutige Daten (wie eine Datenbank-ID) hat, ist dies ein perfekter Kandidat für toString (), so dass es #294: John Doe zurückgeben kann. Aber Einzigartigkeit ist keine Voraussetzung.

Wirklich ... Auch wenn Mister Bloch das sagt ... Ich glaube nicht, dass es sinnvoll ist, Regeln für die Implementierung von toString () zu haben. Es ist sinnvoll für hashCode () und equals (), aber nicht für toString ().

    
kayahr 03.02.2011 18:49
quelle
1

Ja, es ist es wert. ToString hilft dabei, den Zustand des Objekts anschaulich darzustellen. IMO, das ist besonders wichtig in einer Entität, weil ORMs oder andere Bibliotheken von Drittanbietern ein Objekt als Teil ihrer Protokollierungsstrategie ziemlich häufig drucken.

%Vor%

wird offensichtlich implizit toString () aufrufen.

Es hat mir immer wieder geholfen, den Zustand der Entität visuell zu erkennen, um zu bestimmen, ob sie in der Protokollierung transient oder dauerhaft ist, und nur allgemeines Debugging.

Möchten Sie das lieber sehen:

%Vor%

oder das:

%Vor%

Kurz gesagt, der einzige Grund, warum Sie toString nicht überschreiben, ist Faulheit. In den letzten Versionen hat Eclipse sogar einen toString-Generator!

    
hisdrewness 03.02.2011 19:50
quelle

Tags und Links