真的值得为实体类实现 toString() 吗?
始终建议重写(实现)类的方法。toString()
- Java API文档本身说“建议所有子类都覆盖此方法。
- Bloch,在 Effective Java 中有一个项目“Always override toString”。只有傻瓜才会与布洛赫相矛盾,对吧?
然而,我开始怀疑这个建议:它真的值得为实体类实现吗?toString()
我会试着列出我的推理。
-
实体对象具有唯一的标识;它永远不会与另一个对象相同,即使这两个实体具有等效的属性值。也就是说,(对于非空 x),以下不变量适用于实体类(根据定义):
x.equals(y) == (x == y)
该方法返回一个字符串,该字符串“以文本方式表示”其对象(用Java API的单词表示)。
toString()
-
一个好的表示捕获对象的本质,所以如果两个表示不同,它们是不同(非等效)对象的表示,相反,如果两个表示是等价的,它们是等效对象的表示。这为一个好的表示(对于非空 x, y)提出了以下不变性:
x.toString().equals(y.toString()) == x.equals(y)
因此,对于实体,我们期望的是,每个实体对象都应该有一个唯一的文本表示,该表示形式返回。某些实体类将具有唯一的名称或数字 ID 字段,因此它们的方法可以返回包含该名称或数字 ID 的表示形式。但通常,该方法无法访问此类字段。
x.toString().equals(y.toString()) == (x == y)
toString()
toString()
toString()
如果没有实体的唯一字段,最好的办法是包含一个对于不同对象不太可能相同的字段。但这正是
System.identityHashCode()的要求
,这就是所提供的。toString()
Object.toString()
因此,对于没有数据成员的实体对象来说,这是可以的,但是对于大多数类,您可能希望将它们包含在文本表示形式中,对吗?实际上,您希望包含所有这些成员:如果类型具有(非空)数据成员 x,则希望包含在表示中。
Object.toString()
x.toString()
但是,这给持有对其他实体的引用的数据成员带来了问题:即,它们是关联。如果一个对象有一个数据成员,那么幼稚的实现将产生该人的家谱的片段,而不是其本身的片段。如果存在双向关联,则幼稚的实现将递归,直到您获得堆栈溢出,因此可以跳过持有关联的数据成员?
Person
Person father
Person
但是,具有和数据成员的值类型呢?这些关联应由 报告。使所有方法都起作用的最简单方法是仅报告 的标识字段(或)。
Marriage
Person husband
Person wife
Marriage.toString()
toString()
Person.toString()
Person.name
System.identityhashCode(this)
Person
因此,似乎提供的 实现对于实体类来说实际上并不太糟糕。在这种情况下,为什么要覆盖它?
toString()
若要使其具体化,请考虑以下代码:
public final class Person {
public void marry(Person spouse)
{
if (spouse == this) {
throw new IlegalArgumentException(this + " may not marry self");
}
// more...
}
// more...
}
在调试抛出的时,覆盖 的有用程度如何?toString()
IlegalArgumentException
Person.marry()