Effective Java 第三版读书笔记——条款12:总是重写 toString 方法

虽然 Object 类提供了 toString 方法的实现,但它返回的字符串通常不是类的用户想要看到的。它由类名后跟一个 “ at ” 符号(@)和哈希码的无符号十六进制表示组成,例如 PhoneNumber@163b91。toString 的通用约定要求,返回的字符串应该是“一个简洁但内容丰富的表示,对人们来说是很容易阅读的”。提供一个良好的 toString 实现使你的类更易于使用,并且使用此类的系统更易于调试。当对象被传递到 println、printf、字符串连接操作符、断言,或者由调试器打印时,toString 方法会自动被调用。

如果为 PhoneNumber 提供了一个很好的 toString 方法,那么生成一个有用的诊断消息就像下面这样简单:

System.out.println("Failed to connect to " + phoneNumber);

实际上,toString 方法应该返回对象中包含的所有需要关注的信息,如电话号码示例中所示。如果对象很大或者包含不利于字符串表示的状态,包含所有信息就是不切实际的。在这种情况下,toString 应该返回一个摘要,如 Manhattan residential phone directory (1487536 listings) 或线程 [main,5,main]。理想情况下,字符串应该是自解释的(线程示例并没有遵守这点)。

实现 toString 方法时,必须做出的一个重要决定是:是否在文档中指定返回值的格式。建议你对值类(value clsses)进行此操作,例如电话号码或矩阵类。指定格式的好处是它可以作为标准的,明确的,可读的对象表示。这种表示形式可以用于输入、输出以及持久化可读性的数据对象,如 CSV 文件。如果指定了格式,提供一个匹配的静态工厂或构造方法通常是个好主意,这使程序员可以轻松地在对象和字符串表示之间来回转换。Java 平台类库中的许多值类都采用了这种方法,包括 BigInteger、BigDecimal 和大部分基本类型包装类。

指定 toString 返回值的格式的缺点是:假设你的类被广泛使用,那么一旦指定了格式,这个格式就会被一直使用,导致可扩展性很差。程序员将编写代码来解析表达式,生成它,并将其嵌入到持久数据中。如果在将来的版本中更改了格式的表示,那么会破坏他们的代码和数据。但通过选择不指定格式,就可以保留在后续版本中添加信息或改进格式的灵活性。

无论是否决定指定格式,你都应该清楚地在文档中表明你的意图。如果你指定了格式,就要明确地表示出来。例如,这里有一个条款 11 中使用的 PhoneNumber 类的 toString 方法:

/**
 * 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);
}

如果你决定不指定格式,那么文档注释应该是这样的:

/**
 * 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 这三个属性的 getter 方法。如果不这样做,就会强迫需要这些信息的程序员来解析字符串。这降低了程序性能,而且程序员做了不必要的工作,整个过程还很容易出错。

在静态工具类(条款 4)中重写 toString 方法是没有意义的。你也不应该在大多数枚举类型(条款 34)中写一个 toString 方法,因为 Java 为你提供了一个非常好的方法。但是,如果一个抽象类的子类有相同的字符串表示形式,你应该在这个抽象类中重写 toString 方法。例如,大多数集合实现中的 toString 方法都是从抽象集合类中继承的。

回顾一下,除非父类已经这样做了,否则在每个可实例化的类中重写 toString 方法。它使类的使用更加舒适,还可以协助调试。toString 方法应该以一种美观的格式返回对象的简明有用的描述。

猜你喜欢

转载自www.cnblogs.com/LeeFire/p/10014881.html