-
Notifications
You must be signed in to change notification settings - Fork 0
始终要覆写toString
虽然
java.lang.Object提供了toString方法的一个实现, 但它返回的字符串通常并不是用户所期望看到的. 该字符串包含类的名称、以及一个@符号, 接着是散列码的无符号十六进制表示法.
当对象被传递给println、printf、字符串联操作符(+)以及assert或者被调试器打印出来时, toString方法会被自动调用. 如果类提供了好的toString方法, 那么产生有用的诊断消息会非常容易. 相反, 如果没有覆写toString方法, 产生的消息将难以理解.
创建两个具有相同属性的类, 其中一个覆写toString方法, 另外一个不覆写. 其中覆写toString类如代码示例2-1所示:
// 代码示例2-1 // 覆写toString方法的类 public class OverrideToStringVo { private final short areaCode; private final short prefix; private final short lineNumber; // 构造器 public OverrideToStringVo(int areaCode, int prefix, int lineNumber) { rangeCheck(areaCode, 999, "area code"); rangeCheck(prefix, 999, "prefix"); rangeCheck(lineNumber, 9999, "line number"); this.areaCode = (short) areaCode; this.prefix = (short) prefix; this.lineNumber = (short) lineNumber; } // 检查参数是否合法 private static void rangeCheck(int arg, int max, String name) { if (arg < 0 || arg > max) { throw new IllegalArgumentException(name + ": " + arg); } } // 覆写toString方法 @Override public String toString() { return MessageFormat.format("({0,number,000}) {1,number,000}-{2,number,0000}", areaCode, prefix, lineNumber); } }
编写单元测试代码如代码示例2-1所示:
// 代码示例2-1 // 覆写toString方法与不覆写测试代码 @Test public void test() { OverrideToStringVo overrideVo = new OverrideToStringVo(408, 867, 5309); NotOverrideToString notOverrideVo = new NotOverrideToString(408, 867, 5309); Map<String, OverrideToStringVo> orMap = new HashMap<>(1); orMap.put("zachard", overrideVo); Map<String, NotOverrideToString> norMap = new HashMap<>(1); norMap.put("zachard", notOverrideVo); System.err.println("覆写toString方法后, 通过println打印的结果为: "); System.err.println(overrideVo); System.err.println("未覆写toString方法, 通过println打印的结果为: "); System.err.println(notOverrideVo); System.err.println("覆写toString方法后, 通过字符串联操作符(+)打印结果为: " + overrideVo); System.err.println("未覆写toString方法, 通过字符串联操作符(+)打印结果为: " + notOverrideVo); logger.info("覆写toString方法后, 打印Map集合的结果为: {}", orMap); logger.info("未覆写toString方法, 打印Map集合的结果为: {}", norMap); }
执行单元测试, 得到结果如下:
覆写toString方法后, 通过println打印的结果为:
(408) 867-5309
未覆写toString方法, 通过println打印的结果为:
com.zachard.effective.java.general.method.NotOverrideToString@51e2adc7
覆写toString方法后, 通过字符串联操作符(+)打印结果为: (408) 867-5309
未覆写toString方法, 通过字符串联操作符(+)打印结果为: com.zachard.effective.java.general.method.NotOverrideToString@51e2adc7
覆写toString方法后, 打印Map集合的结果为: {zachard=(408) 867-5309}
未覆写toString方法, 打印Map集合的结果为: {zachard=com.zachard.effective.java.general.method.NotOverrideToString@51e2adc7}
从测试结果可以看出, 覆写toString方法之后, 通过println及+等操作得到的信息更为简洁、易于理解.
如果对象太大, 或者对象中包含的状态信息难以用字符串表示, 这样做就有点不切实际. 在这种情况下,
toString应该返回一个摘要信息.
指定格式的好处是: 它可以被用作一种标准的、明确的、适合人阅读的对象表示法. 这种表示法可以用于输入和输出, 以及用在永久的适合于人类阅读的数据对象中, 例如XML文档.
如果指定了格式, 最好再提供一个相匹配的静态工厂或是构造器, 以便程序员可以很容易地在对象和它的字符串表示法之间来回转换. 例如为
代码示例2-1提供一个用于转换的构造器如代码示例3-1所示:
// 代码示例3-1 // 将toString方法生成的字符串转换为对象的构造器 public OverrideToStringVo(String string) throws ParseException { Object[] fields = MESSAGE_FORMAT.parse(string); this.areaCode = ((Long) fields[0]).shortValue(); this.prefix = ((Long) fields[1]).shortValue(); this.lineNumber = ((Long) fields[2]).shortValue(); }
编写相应的单元测试代码如代码示例3-2所示:
// 代码示例3-2 // 测试toString生成的字符串构造器 @Test public void string2ObjectTest() throws ParseException { OverrideToStringVo preVo = new OverrideToStringVo(408, 867, 5309); String strVal = preVo.toString(); logger.info("将toString按格式打印的字符串转换为对象测试, 初始对象为: {}", preVo); OverrideToStringVo postVo = new OverrideToStringVo(strVal); logger.info("将toString按格式打印的字符串转换为对象测试, 由字符串转换得到的对象各个域的值为: " + "areaCode: {}, prefix: {}, lineNumber: {}", postVo.getAreaCode(), postVo.getPrefix(), postVo.getLineNumber()); }
测试结果如下:
将toString按格式打印的字符串转换为对象测试, 初始对象为: (408) 867-5309
将toString按格式打印的字符串转换为对象测试, 由字符串转换得到的对象各个域的值为: areaCode: 408, prefix: 867, lineNumber: 5309
指定格式的不足是: 如果这个类已经被广泛使用, 一旦指定格式, 就必须始终如一的坚持这种格式. 程序员将会编写出相应的代码来解析这个字符串表示法、产生字符串表示法, 以及把字符串表示法嵌入到持久的数据中. 如果将来的发行版本中改变了这种表示法, 就会破环原有的代码和数据. 而不指定格式就可以保证灵活性, 便于在将来的发行版本中增加信息, 或者改进格式.
鉴于目前大多数前后端交互都使用
JSON格式传输数据, 所以在需要指定格式时, 可以考虑采用JSON格式.
即: 在类中提供从toString打印的字符串中直接获取对应类属性值的方法. 如果不这么做, 就会迫使那些需要这些信息的程序员不得不自己去解析这些字符串. 除了降低了程序的性能, 使得程序员去做这些不必要的工作之外, 这个解析过程也很容易出错.