14

It is consistently advised to override (implement) the toString() method of a class.

  • The Java API documentation itself says "It is recommended that all subclasses override this method.".
  • Bloch, in Effective Java has the item "Always override toString". And only a fool contradicts Bloch, right?

I am however coming to doubt this advice: is it really worth implementing toString() for entity classes?


I'll try to lay out my reasoning.

  1. An entity object has a unique identity; it is never the same as another object, even if the two entites have equivalent attribute values. That is, (for non-null x), the following invariant applies for an entity class (by definition):

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

  2. The toString() method returns a string that "textually represents" its object (in the words of the Java API).

  3. A good representation captures the essentials of the object, so if two representations are different they are representaions of different (non-equivalent) objects, and conversely if two represenations are equivalent they are representations of equivalent objects. That suggests the following invariant for a good representation (for non-null x, y):

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

  4. Thus for entities we expect x.toString().equals(y.toString()) == (x == y) that is, each entity object should have a unique textual representation, which toString() returns. Some entity classes will have a unique name or numeric ID field, so their toString() method could return a representation that includes that name or numeric ID. But in general, the toString() method does not have access to such a field.

  5. Without a unique field for an entity, the best that toString() can do is to include a field that is unlikely to be the same for different objects. But that is exactly the requirement of System.identityHashCode(), which is what Object.toString() provides.

  6. So Object.toString() is OK for an entity object that has no data members, but for most classes you would want to include them in the text representation, right? In fact, you'd want to include all of them: if the type has a (non null) data member x, you would want to include x.toString() in the representation.

  7. But this creates a problem for data members that hold references to other entities: that is, which are associations. If a Person object has a Person father data member, the naive implementation will produce a fragment of that person's family tree, not of the Person itself. If there are two-way assocaitions, a naive implementation will recurse until you get stack overflow So maybe skip the data members that hold associations?

  8. But what about a value type Marriage having Person husband and Person wife data members? Those associations ought to be reported by Marriage.toString(). The simplest way to make all the toString() methods work is for Person.toString() to report only the identity fields (Person.name or System.identityhashCode(this)) of the Person.

  9. So it seems that the provided implementation of toString() is actually not too bad for entity classes. In that case, why override it?


To make it concrete, consider the following code:

public final class Person {

   public void marry(Person spouse)
   {
      if (spouse == this) {
         throw new IlegalArgumentException(this + " may not marry self");
      }
      // more...
   }

   // more...
}

Just how useful would an override of toString() be when debugging an IlegalArgumentException thrown by Person.marry()?

Community
  • 1
  • 1
Raedwald
  • 46,613
  • 43
  • 151
  • 237
  • 1
    I disagree with your point #3. You do not always want all properties to be shown in the string representation. – finnw Feb 03 '11 at 18:56
  • 1
    The formal definition you're using for the word entity would be helpful in understanding your argument :) – Affe Feb 03 '11 at 19:05
  • 3
    Loved you statement about Bloch and fools :) Not arguing that these guys are smart, etc, but just remember EntityBeans in EJB2.x. That was disaster :) Generally, I would avoid using any "good practices", unless there is a good reason for it. – Stas Feb 03 '11 at 19:23
  • >>Bloch, in Effective Java has the item "Always override toString". And only a fool contradicts Bloch, right?<< Consider me a fool, no offense taken! – bestsss Feb 03 '11 at 19:55
  • How did you come up with #3? I quickly checked the relevant section in "Effective Java", but I couldn't find that statement - and would have been surprised if I had... – Puce Feb 03 '11 at 19:56
  • @Raedwald: Your example error message doesn't make sense to me. A more useful one might be `"Illegal request for " + this + " to marry " + spouse + ": A Person cannot marry itself"`. But either way it's not a good example of when `toString()` would be useful since `marry()` has full access to the internals of `Person` to give a more deliberate message. – Mark Peters Feb 03 '11 at 20:18
  • I find it hard to understand your logic. Just because you don't have to provide an exceptionally useful feature in your class is no reason to avoid doing so. – Highland Mark Feb 03 '11 at 21:28
  • @Highland MarK: "Just because you don't have to provide an exceptionally useful feature": but I'd question whether an override was exceptionally useful for entity classes. – Raedwald Feb 03 '11 at 21:43
  • It may not be /exceptionally/ useful, but using IDEA (and probably other IDEs) a generally useful toString() can be trivially added in less time that it took to add this comment, let alone the original posting... – Gwyn Evans Feb 03 '11 at 22:23
  • Your 'Person' can have a name, getting 'Paul may not marry self"' instead of '@472357 may not marry self"' could be more readable. You decide what 'this' could be as a string and what could be the most explanatory. – Tokazio Mar 31 '16 at 13:44
  • @Tokazio However, as `Person` names are not unique, they are not useful as identifiers. You wouldn't use the name of a person as a primary key in a database table. – Raedwald Mar 31 '16 at 14:48
  • @Raedwald: of course, that's an example to say that a name or description is more explanatory than a memory address ;) – Tokazio Mar 31 '16 at 14:50

5 Answers5

11

So it seems that the provided implementation of toString() is actually not too bad for entity classes. In that case, why override it?

What makes you think the goal of toString() is simply to have a unique String? That's not its purpose. Its purpose is to give you context about the instance, and simply a classname and hashcode don't give you context.

Edit

Just want to say that I by no means think you need to override toString() on every object. Valueless objects (like a concrete implementation of a listener or a strategy) need not override toString() since every single instance is indistinguishable from any other, meaning the class name is sufficient.

Mark Peters
  • 80,126
  • 17
  • 159
  • 190
  • "give you context": but it seems to me that information about the entities is not so useful. Contrast with a precondition check in a method, throwing a suitable `IlegalArgumentException`. I suggest that the precondition would be about value objects and the identity or otherwise of entities, in which case using the provided `toString()` in the exception message is not too bad for the entities. – Raedwald Feb 03 '11 at 19:18
  • @Raedwald: maybe you should give a practical example. Terms like "entity class" have a tendency of being thrown around loosely. – Mark Peters Feb 03 '11 at 19:41
10

Point # 3 is the weak link in this argument, and I in fact violently disagree with it. Your invariant is (reordered)

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

I would say, rather:

x.equals(y) → x.toString().equals(y.toString()); 

That is, logical implication. If x and y are equal, their toString()s should be equal, but an equal toString() does not necessarily mean that the objects are equal (think of the equals():hashCode() relationship; equal objects must have the same hash code, but same hash code can not be taken to mean the objects are equal).

Fundamentally, toString() doesn't really have any 'meaning' in a programmatic sense, and I think you're trying to imbue it with one. toString() is most useful as a tool for logging etc; you ask how useful an overridden toString() would be given:

throw new IlegalArgumentException(this + " may not marry self");

I would say it's massively useful. Say you notice a lot of errors in your logs and see:

IllegalArgumentException: com.foo.Person@1234ABCD cannot marry self
IllegalArgumentException: com.foo.Person@2345BCDE cannot marry self
IllegalArgumentException: com.foo.Person@3456CDEF cannot marry self
IllegalArgumentException: com.foo.Person@4567DEFA cannot marry self

what do you do? You have no idea at all what's going on. If you see:

IllegalArgumentException: Person["Fred Smith", id=678] cannot marry self
IllegalArgumentException: Person["Mary Smith", id=679] cannot marry self
IllegalArgumentException: Person["Mustafa Smith", id=680] cannot marry self
IllegalArgumentException: Person["Emily-Anne Smith", id=681] cannot marry self

then you actually have some chance of working out what's going on ('Hey, someone is trying to make the Smith family marry themselves') and that may actually help with debugging etc. Java object IDs give you no information at all.

Cowan
  • 37,227
  • 11
  • 66
  • 65
  • +1 Others have touched on #3 being wrong, but your reply is the clearest indication of the problem. – Raedwald Feb 03 '11 at 22:22
  • However, note my #4 & #5: when there are "ID like" fields, it seems clear to me that the `toString()` method should display them. Your example of the Smith family demonstrates this using `Person.name`. But what if there are no such fields? – Raedwald Feb 03 '11 at 22:26
  • I think I need you to clarify what you mean by an 'Entity' object. If you're talking about an EJB or JPA Entity, they always have some sort of ID field, because by definition a JPA Entity has a primary key which can be used to uniquely identify it. And it's the primary key which is probably MOST useful to have in the logs... – Cowan Feb 04 '11 at 05:43
  • `x.equals(y) == (x == y)` is the definition of entity here. – Raedwald Feb 04 '11 at 17:08
  • @Raedwald: I would posit that you're applying the term "entity" to two distinct major kinds of things, one of which I would call a "mutable data holder". Distinct instances of a mutable data holder type are generally not equivalent, even if they hold identical data, but two objects which each hold references of such type may be equivalent if the targets of those references hold identical data and *will never be exposed to anything that could mutate them*. My contrast, objects holding references to distinct entity instances are never equivalent. – supercat Nov 20 '12 at 20:10
3

Having a toString() method in entity classes can be tremendously helpful for debugging purposes. From a practical standpoint, using IDE templates or something like the Project Lombok @ToString annotation simplifies this a lot and makes it easy to implement quickly.

Kai Sternad
  • 22,214
  • 7
  • 47
  • 42
  • "can be tremendously helpful for debugging purposes" is vague. What aspects of a `toString()` method for **an entity class** do you think is generally useful for debugging? – Raedwald Feb 03 '11 at 19:13
  • E.g. when you unit test if the object graph retrieved by JPA is equals to an expected object graph – Puce Feb 03 '11 at 19:48
  • "equals to an expected" means the only relevant relationship is `x.equals(y)`, which for the entities is `x==y`. So all you need is a `toString()` representation that shows whether `x==y`: `Object.toString()` does a good approximation of that. – Raedwald Feb 03 '11 at 20:01
3

I always use toString() for my own purposes and not because of some technical requirement. When I have a Person class then the toString method returns the name of the person. No more, no less. It's not unique but for debugging purposes it is enough to see what person is meant. Especially in web development this comes in very handy when I just have to write the object name in JSPs to get the name of the person so I know I have the correct object.

If the object has some unique data (like a database ID) then this is a perfect candidate for toString() so it could return #294: John Doe. But uniqueness isn't a requirement.

Really... Even if Mister Bloch says so... I don't think it makes sense to have any rules for implementing toString(). It makes sense for hashCode() and equals() but not for toString().

kayahr
  • 20,913
  • 29
  • 99
  • 147
3

Yes it's worth it. ToString helps give tangible, visual output to the state of the object. IMO, it's especially important in an entity, because ORMs or other third party libraries print an object as a part of their logging strategy quite frequently.

logger.debug("Entity: {}", entity);

will obviously implicitly call toString().

It has helped me time and time again to visually see the state of the entity to determine whether or not it's transient or persistent in the logging in terms of transactionality, and just general debugging.

Would you rather see this:

DEBUG | pattern: test.entity.MyEntity@12345f

or this:

DEBUG | pattern: MyEntity [id = 1234.0, foo=bar, bar=baz]

In short, the only reason you wouldn't override toString is laziness. In recent releases, Eclipse even has a toString generator!

hisdrewness
  • 7,599
  • 2
  • 21
  • 28
  • Reason to include the values that constitute the primary key of the object, but not any others. I already mentioned "a representation that includes that name or numeric ID". – Raedwald Feb 03 '11 at 19:57