Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

始终要覆写toString

zachard edited this page Mar 19, 2018 · 3 revisions

虽然java.lang.Object提供了toString方法的一个实现, 但它返回的字符串通常并不是用户所期望看到的. 该字符串包含类的名称、以及一个@符号, 接着是散列码的无符号十六进制表示法.

1. toString方法的通用约定

(1.1) toString返回的字符串应该是一个简洁的, 但信息丰富, 并且易于阅读的表达形式.

(1.2) 建议所有的子类都覆写toString方法.

2. 覆写toString方法的好处

(2.1) 好的toString实现可以使类用起来更加舒适.

当对象被传递给printlnprintf、字符串联操作符(+)以及assert或者被调试器打印出来时, toString方法会被自动调用. 如果类提供了好的toString方法, 那么产生有用的诊断消息会非常容易. 相反, 如果没有覆写toString方法, 产生的消息将难以理解.

(2.2) 覆写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+等操作得到的信息更为简洁、易于理解.

3. 覆写toString应该注意的事项

(3.1) toString方法应该返回对象中包含的所有值得关注的信息.

如果对象太大, 或者对象中包含的状态信息难以用字符串表示, 这样做就有点不切实际. 在这种情况下, toString应该返回一个摘要信息.

(3.2) 慎重决定是否要在文档中指定返回值格式.

指定格式的好处是: 它可以被用作一种标准的、明确的、适合人阅读的对象表示法. 这种表示法可以用于输入和输出, 以及用在永久的适合于人类阅读的数据对象中, 例如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格式.

(3.3) 无论是否指定格式, 都应该在文档中明确的表明toString方法的意图.

(3.4) 无论是否指定格式, 都应该为toString返回值中包含的所有信息提供一种编程式的访问途径.

即: 在类中提供从toString打印的字符串中直接获取对应类属性值的方法. 如果不这么做, 就会迫使那些需要这些信息的程序员不得不自己去解析这些字符串. 除了降低了程序的性能, 使得程序员去做这些不必要的工作之外, 这个解析过程也很容易出错.

文档索引

Clone this wiki locally

AltStyle によって変換されたページ (->オリジナル) /