Java高效编程(12):重写toString方法
解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界
尽管 Object
类提供了 toString
方法的默认实现,但它返回的字符串通常不是类的使用者想要看到的。默认返回的字符串格式是类名加上“@”符号和哈希码的十六进制表示,例如 PhoneNumber@163b91
。然而,toString
方法的合同要求返回一个简洁且信息丰富的字符串表示,以便用户阅读。显然,与“707-867-5309”相比,“PhoneNumber@163b91”并不具备太多的可读性和信息量。因此,建议重写 toString
方法。
虽然重写 toString
不如重写 equals
和 hashCode
那样重要(如【条目10】和【条目11】所述),但提供一个良好的 toString
实现能让你的类更加易用,且有助于调试。当对象被传递给 println
、printf
、字符串连接操作符或断言语句时,toString
会自动调用。即使你不主动调用 toString
,其他人可能会。例如,某个组件在记录错误消息时可能会包含对象的字符串表示。如果你没有重写 toString
,这些消息几乎是无用的。
为什么要重写 toString
如果你为 PhoneNumber
提供了一个好的 toString
方法,那么生成有用的诊断消息会变得非常简单:
System.out.println("Failed to connect to " + phoneNumber);
不论你是否重写 toString
,程序员通常都会以这种方式生成诊断消息,但只有当你重写了 toString
,这些消息才真正有用。toString
的好处不仅体现在类的实例本身,也会反映在包含这些实例的对象上,特别是集合。比如,当你打印一个 Map
时,哪一个更可读:{Jenny=PhoneNumber@163b91}
还是 {Jenny=707-867-5309}
?
toString
方法应尽可能返回对象中的所有有趣信息。对于较大的对象或不适合字符串表示的对象,可以返回摘要信息,例如 Manhattan residential phone directory (1487536 listings)
或 Thread[main,5,main]
。理想情况下,字符串应该是自解释的。
格式化和文档化
实现 toString
时,一个重要的决定是是否要在文档中指定返回值的格式。对于像电话号码这样的值类,建议指定格式,因为这可以为对象提供一种标准且明确的表示,可以用于输入、输出以及持久化数据(如 CSV 文件)。如果你指定了格式,最好提供匹配的静态工厂方法或构造函数,以便程序员能够在对象和字符串表示之间轻松转换。许多 Java 平台库中的值类(如 BigInteger
、BigDecimal
和大多数包装类)都采用了这种方法。
指定 toString
格式的缺点是,一旦你指定了格式,就必须长期坚持使用,特别是当你的类被广泛使用时。如果将来修改了表示格式,可能会导致程序崩溃或数据损坏。因此,如果你不指定格式,就保留了将来改进格式的灵活性。
无论是否指定格式,都应该明确记录你的意图。如果指定格式,要精确说明。例如,下面是与【条目11】中的 PhoneNumber
类相匹配的 toString
方法文档:
/*** 返回该电话号码的字符串表示。* 字符串由十二个字符组成,格式为“XXX-YYY-ZZZZ”,* 其中 XXX 是区号,YYY 是前缀,ZZZZ 是号码。* * 如果这三个部分中的任意一部分不够长,会用前导零填充。* 例如,如果号码值是 123,字符串表示的最后四个字符将是“0123”。*/
@Override
public String toString() {return String.format("%03d-%03d-%04d", areaCode, prefix, lineNum);
}
如果你选择不指定格式,则文档注释可能会类似如下:
/*** 返回该药水的简短描述。表示的具体细节未指定,并且可能会变化,* 但通常的格式可能如下:** "[Potion #9: type=love, smell=turpentine, look=india ink]"*/
@Override
public String toString() {// 自定义实现
}
这种文档确保了使用者在解析字符串表示时不能抱怨格式变化引发的问题。
提供访问器
无论是否指定 toString
格式,都应提供程序化访问对象信息的途径。以 PhoneNumber
类为例,应该提供获取区号、前缀和号码的访问器。如果没有这些访问器,程序员不得不解析字符串来获取这些信息,这不仅降低了性能,还可能导致错误的系统。如果没有访问器,即使你声明字符串格式是可变的,它实际上仍然会成为一个事实上的 API。
特殊情况
不必在静态工具类(【条目4】)或大多数 enum
类型中编写 toString
方法,因为 Java 已经为 enum
提供了一个很好的默认实现。不过,在抽象类中,如果子类共享相同的字符串表示形式,则应该编写 toString
方法。例如,许多集合实现继承了抽象集合类的 toString
实现。
此外,Google 的开源 AutoValue
工具可以自动生成 toString
方法,绝大多数 IDE 也提供了自动生成 toString
的功能。这些自动生成的方法对于了解类的字段内容非常有用,但并不适合表示类的特定含义。例如,电话号码类应该有一个标准的字符串表示,而药水类则可以接受自动生成的 toString
方法。不过,自动生成的 toString
方法仍然比继承自 Object
的默认实现好得多,后者几乎无法提供任何有用信息。
总结
除非超类已经重写了 toString
,否则在你编写的每个实例类中都应重写 toString
。这会让你的类更易用,并有助于调试。toString
方法应返回对象的简洁、有用的描述,并且格式美观。