Tips
《Effective Java, Third Edition》一书英文版已经出版,这本书的第二版想必不少人都读过,号称Java四大名著之一,不过第二版2009年出版,到如今已经将近8年的时间,但随着Java 6,7,8,甚至9的发布,Java语言发生了深入的变化。
在这里第一时间翻译成中文版。供你们学习分享之用。git
虽然Object类提供了toString方法的实现,但它返回的字符串一般不是你的类的用户想要看到的。 它由类名后跟一个“at”符号(@)和哈希码的无符号十六进制表示组成,例如PhoneNumber@163b91
。 toString的通用约定要求,返回的字符串应该是“一个简洁但内容丰富的表示,对人们来讲是很容易阅读的”。虽然能够认为PhoneNumber@163b91
简洁易读,但相比于707-867-5309
,但并非很丰富 。 toString通用约定“建议全部的子类重写这个方法”。好的建议,的确如此!程序员
虽然它并不像遵照equals和hashCode约定那样重要(条目 10和11),可是提供一个良好的toString实现使你的类更易于使用,并对使用此类的系统更易于调试。当对象被传递到println、printf、字符串链接操做符或断言,或者由调试器打印时,toString方法会自动被调用。即便你从不调用对象上的toString,其余人也能够。例如,对对象有引用的组件可能包含在日志错误消息中对象的字符串表示。若是未能重写toString,则消息多是无用的。编程
若是为PhoneNumber
提供了一个很好的toString方法,那么生成一个有用的诊断消息就像下面这样简单:api
System.out.println("Failed to connect to " + phoneNumber);
程序员将以这种方式生成诊断消息,无论你是否重写toString,可是除非你这样作,不然这些消息将不会有用。 提供一个很好的toString方法的好处不只包括类的实例,一样有益于包含实例引用的对象,特别是集合。 打印map 对象时你会看到哪个,{Jenny=PhoneNumber@163b91}
仍是{Jenny=707-867-5309}
?ide
实际上,toString方法应该返回对象中包含的全部须要关注的信息,如电话号码示例中所示。 若是对象很大或者包含不利于字符串表示的状态,这是不切实际的。 在这种状况下,toString应该返回一个摘要,如 Manhattan residential phone directory (1487536 listings)
或线程[main,5,main]
。 理想状况下,字符串应该是不言自明的(线程示例并无遵照这点)。 若是未能将全部对象的值得关注的信息包含在字符串表示中,则会致使一个特别烦人的处罚:测试失败报告以下所示:工具
Assertion failure: expected {abc, 123}, but was {abc, 123}.
实现toString方法时,必须作出的一个重要决定是:在文档中指定返回值的格式。 建议你对值类进行此操做,例如电话号码或矩阵类。 指定格式的好处是它能够做为标准的,明确的,可读的对象表示。 这种表示形式能够用于输入、输出以及持久化可读性的数据对象,如CSV文件。 若是指定了格式,一般提供一个匹配的静态工厂或构造方法,是个好主意,因此程序员能够轻松地在对象和字符串表示之间来回转换。 Java平台类库中的许多值类都采用了这种方法,包括BigInteger,BigDecimal和大部分基本类型包装类。性能
指定toString返回值的格式的缺点是,假设你的类被普遍使用,一旦指定了格式,就会终身使用。程序员将编写代码来解析表达式,生成它,并将其嵌入到持久数据中。若是在未来的版本中更改了格式的表示,那么会破坏他们的代码和数据,而且还会抱怨。但经过选择不指定格式,就能够保留在后续版本中添加信息或改进格式的灵活性。学习
不管是否决定指定格式,你都应该清楚地在文档中代表你的意图。若是指定了格式,则应该这样作。例如,这里有一个toString方法,该方法在条目 11中使用PhoneNumber
类:测试
/** * Returns the string representation of this phone number. * The string consists of twelve 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". */ @Override public String toString() { return String.format("%03d-%03d-%04d", areaCode, prefix, lineNum); }
若是你决定不指定格式,那么文档注释应该是这样的:this
/** * 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返回的值中包含的信息。 例如,PhoneNumber
类应该包含 areaCode, prefix, lineNum这三个属性。 若是不这样作,就会强迫程序员须要这些信息来解析字符串。 除了下降性能和程序员作没必要要的工做以外,这个过程很容易出错,若是改变格式就会中断,并致使脆弱的系统。 因为未能提供访问器,即便已指定格式可能会更改,也能够将字符串格式转换为事实上的API。
在静态工具类(条目 4)中编写toString方法是没有意义的。 你也不该该在大多数枚举类型(条目 34)中写一个toString方法,由于Java为你提供了一个很是好的方法。 可是,你应该在任何抽象类中定义toString方法,该类的子类共享一个公共字符串表示形式。 例如,大多数集合实现上的toString方法都是从抽象集合类继承的。
Google的开放源代码AutoValue工具在条目 10中讨论过,它为你生成一个toString方法,就像大多数IDE工具同样。 这些方法很是适合告诉你每一个属性的内容,但并非专门针对类的含义。 所以,例如,为咱们的PhoneNumber
类使用自动生成的toString方法是不合适的(由于电话号码具备标准的字符串表示形式),可是对于咱们的Potion
类来讲,这是彻底能够接受的。 也就是说,自动生成的toString方法比从Object继承的方法要好得多,它不会告诉你对象的值。
回顾一下,除非父类已经这样作了,不然在每一个实例化的类中重写Object的toString实现。 它使得类更加温馨地使用和协助调试。 toString方法应该以一种美观的格式返回对象的简明有用的描述。