是否真的值得为实体类实现toString()

Rae*_*ald 10 java debugging tostring

始终建议覆盖(实现)toString()类的方法.

  • Java API文档本身说:"我们建议所有子类重写此方法."
  • 有效Java中的Bloch 具有"始终覆盖toString"项.只有傻瓜才与布洛赫相矛盾,对吧?

然而,我怀疑这个建议:它真的值得toString() 为实体类实现吗?


我会尝试列出我的推理.

  1. 一个实体对象具有唯一的标识; 即使两个entites具有相同的属性值,它也永远不会与另一个对象相同.也就是说,(对于非null x),以下不变量适用于实体类(根据定义):

    x.equals(y) == (x == y)

  2. toString()方法返回一个"文本表示"其对象的字符串(用Java API的话说).

  3. 表示捕获对象的要领,所以如果两个表示是不同的它们是不同的(非当量)对象representaions,反之如果两个represenations是等价它们是等效的对象的表示.这表明以下对于良好表示的不变性(对于非空x,y):

    x.toString().equals(y.toString()) == x.equals(y)

  4. 因此,对于我们期望的实体 x.toString().equals(y.toString()) == (x == y) ,每个实体对象应该具有唯一的文本表示,其toString()返回.某些实体类将具有唯一的名称或数字ID字段,因此它们的toString()方法可以返回包含该名称或数字ID的表示.但一般而言,该toString()方法无法访问此类字段.

  5. 如果没有实体的唯一字段,那么toString()可以做的最好的事情就是包含一个对于不同对象不太可能相同的字段.但是,这是完全的要求System.identityHashCode(),这是什么Object.toString()规定.

  6. 因此,Object.toString()对于那些没有任何数据成员的实体对象确定,但对于大多数类,你会希望包括他们在文中表示,对不对?实际上,您希望包含所有这些:如果类型具有(非null)数据成员x,您可能希望包含x.toString()在表示中.

  7. 但这会对持有对其他实体的引用的数据成员产生问题:即哪些是关联.如果一个Person对象有一个Person father数据成员,那么天真的实现将产生该人的家谱的片段,而不是Person它自己.如果存在双向关联,那么天真的实现将会递归,直到您获得堆栈溢出所以可能会跳过持有关联的数据成员?

  8. 但是Marriage拥有Person husbandPerson wife数据成员的价值类型呢?那些协会应该由报道Marriage.toString().使所有toString()方法工作的最简单方法是Person.toString()仅报告标识字段(Person.nameSystem.identityhashCode(this))Person.

  9. 因此,toString()对于实体类来说,提供的实现似乎实际上并不太糟糕.在那种情况下,为什么要覆盖呢?


要使其具体,请考虑以下代码:

public final class Person {

   public void marry(Person spouse)
   {
      if (spouse == this) {
         throw new IlegalArgumentException(this + " may not marry self");
      }
      // more...
   }

   // more...
}
Run Code Online (Sandbox Code Playgroud)

toString()在调试IlegalArgumentException抛出时,覆盖是多么有用Person.marry()

Mar*_*ers 10

所以似乎提供的toString()实现对于实体类来说实际上并不是太糟糕.在那种情况下,为什么要覆盖呢?

是什么让你认为目标toString()只是拥有一个独特的字符串?这不是它的目的.它的目的是为您提供有关实例的上下文,并且只是类名和哈希码不会为您提供上下文.

编辑

只想说,我绝不认为你需要重写toString()每一个对象.无值对象(如侦听器或策略的具体实现)无需覆盖,toString()因为每个实例都与其他实例无法区分,这意味着类名就足够了.


Cow*_*wan 9

第3点是这个论点中的薄弱环节,事实上我对此持不同意见.你的不变量是(重新排序)

x.equals(y) == x.toString().equals(y.toString()); 
Run Code Online (Sandbox Code Playgroud)

我会说,而是:

x.equals(y) ? x.toString().equals(y.toString()); 
Run Code Online (Sandbox Code Playgroud)

这就是逻辑含义.如果x和y都是平等的,他们的toString()秒应该是平等的,但相等的toString()并没有一定意味着对象是相等的(认为的equals():hashCode()关系;相等的对象必须具有相同的哈希代码,但相同的散列代码不能被视为对象是相等的).

从根本toString()上说,在程序意义上并没有任何"意义",我认为你试图用它来灌输它.toString()作为记录等工具最有用; 你问一下被覆盖的有用之处toString():

throw new IlegalArgumentException(this + " may not marry self");
Run Code Online (Sandbox Code Playgroud)

我会说这是大量有用的.假设您在日志中发现了很多错误并看到:

IllegalArgumentException: com.foo.Person@1234ABCD cannot marry self
IllegalArgumentException: com.foo.Person@2345BCDE cannot marry self
IllegalArgumentException: com.foo.Person@3456CDEF cannot marry self
IllegalArgumentException: com.foo.Person@4567DEFA cannot marry self
Run Code Online (Sandbox Code Playgroud)

你是做什么?你根本不知道发生了什么.如果你看到:

IllegalArgumentException: Person["Fred Smith", id=678] cannot marry self
IllegalArgumentException: Person["Mary Smith", id=679] cannot marry self
IllegalArgumentException: Person["Mustafa Smith", id=680] cannot marry self
IllegalArgumentException: Person["Emily-Anne Smith", id=681] cannot marry self
Run Code Online (Sandbox Code Playgroud)

然后你实际上有机会弄清楚发生了什么('嘿,有人试图让史密斯家族自己结婚')这实际上可能有助于调试等.Java对象ID 根本不会给你任何信息.