@XingdingCAO
2018-02-13T15:36:25.000000Z
字数 2590
阅读 1616
Java
虽然java.lang.Object
提供了toString
这一方法的实现,但是该方法返回的值却通常不是用户想看到的。这个值由类名、一个@
以及该实例的无符号十六进制的哈希码组成,例如:PhoneNumber@163b91
。
toString
的协议如下,其中重要的一点就是返回一个简短但又翔实的、易于理解的表述。PhoneNumber@163b91
既简洁又易读,但是并不能提供多少有用的信息。相比之下,直接给出电话号码(+86)18888888888
就有用多了。
此外,协议建议所有子类都重写这个方法,确实应该这样做。
虽然比不上遵守hashcode
equals
协议的重要性,但是提供一个良好地toString
实现会让你的类使用起来更令人愉悦。
toString
方法在使用println
printf
、字符串连接符+
、assert
或被调试器输出时自动调用。
printf
方法自Java 1.5起被添加进平台中,相关的方法还有String.format
,这个方法大致等同于C语言中的sprintf
函数。
假如你提供了一个良好的toString
方法,那么生成下面的诊断信息就会十分简单:
System.out.println("Failed to connect: " + phoneNumber)
无论你是否恰当地实现了toString
方法,程序员都会以这种形式生成诊断信息。
良好的toString
的受益者不限于一个类的实例,更包括含有实例引用的对象,尤其是集合对象。当你输出一个map
时,哪一个你更愿意看到:{phoneNumber=PhoneNumber@163b91}
还是{phoneNumber=(+86)18888888888}
?
若实际可行,toString
方法应该包含对象中所有令人感兴趣的信息。但是,当对象过于庞大,又或者对象包含了难以转换为文字的状态时,这一目标并不可行。这些情形下,toString
应该返回一个概要,例如:“Manhattan white pages (1487536 listings)” “Thread[main, 5, main]”。
理想的情况下,产生的文本应该是一目了然的。(可惜,Thread
并未通过本项)
在实现toString
时,需要做出一个重要的决定:是否确定返回文本的格式,以及将其写入文档中。通常对于value classes的建议是肯定的。例如:电话号码、矩阵等。
明确格式的好处是:格式作为一个标准,消除了歧义,使得可读性提高。格式可以用于输入或输出,以及一些持久化的、可读的数据对象中(例如:XML文件)。
如果你确定了格式,那么最好也提供一个与之匹配的静态工厂或构造方法,以此来方便对象与其文本表示的相互转化。这种方法被Java平台的类库中的众多value class所采用,例如:BigInteger
BigDecimal
以及大多数装箱的原始类型。
明确toString
返回值的格式的劣势就是,一旦你明确了格式,那么你将一直陷入其中(假定你的类被广泛使用)。程序员会编写转换表述的代码,生成之后,将会裹入持久的数据中。如果你在新的发行版中更改了表述,那么代码和数据之间的纽带将会被破坏。不去明确文本的格式,你保有在接下来的发行中添加或改进格式的灵活。
无论你决定去明确格式,你都应该明确地用文档表明你的意图。如果你决定明确格式,那么你应该非常细致。
/**
* Returns the string representation of this phone number.
* The string consists of fourteen characters whose format
* is "(XXX) YYY-ZZZZ", where XXX is the area code, YYY is
* the prefix, and ZZZZ is the line number. (Each of the
* capital letters represents a single decimal digit.)
*
* If any of the three parts of this phone number is too small
* to fill up its field, the field is padded with leading zeros.
* For example, if the value of the line number is 123, the last
* four characters of the string representation will be "0123".
*
* Note that there is a single space separating the closing
* parenthesis after the area code from the first digit of the
* prefix.
*/
@Override
public String toString() {
return String.format("(%03d) %03d-%04d", areaCode, prefix, lineNumber);
}
如果不决定明确格式,那么文档应该如下:
/**
* Returns a brief description of this potion. The exact details
* of the representation are unspecified and subject to change,
* but the following may be regarded as typical:
*
* "[Potion #9: type=love, smell=turpentine, look=india ink]"
*/
@Override
public String toString()
{ ... }
无论你决定去明确格式,你都应该提供所有对于toString
返回文本中包含的信息的程序性的访问入口。例如,手机号码返回的(+86)18888888888
包含了区号和连线号码,那么就该类就应该有访问区号和连线号码的入口。否则,你在逼迫需要信息的程序员去转换这个字符串。这样做,除了会降低性能、为程序员带来了不必要的工作,而且处理过程也是易出错的,还导致了系统的脆弱。一旦你更改了格式,系统就分崩离析。若不去提供访问子,你已经将格式转变为事实上的API,即使你已经指出了格式可能变动的可能。