title: What is a 'canonical representation' of a field meant to be for equals() method (Joshua Bloch)
body: <p>In chapter 3, item 8:</p>
  
  <pre><code>public final class CaseInsensitiveString {
      private final String s;
  
      public CaseInsensitiveString(String s) {
          if (s == null)
              throw new NullPointerException();
          this.s = s;
      }
  
      @Override public boolean equals(Object o) {
          return o instanceof CaseInsensitiveString &amp;&amp;
              ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
      }
      // remainder omitted
  }
  </code></pre>
  
  <p>After describing issues surrounding the <code>equals()</code> method, he goes on to talk about this class in the context of comparing fields. </p>
  
  <blockquote>
    <p>For some classes, such as CaseInsensitiveString above, field comparisons are more complex than simple equality tests. If this is the case, you may want to store a canonical form of the field, so the <code>equals()</code> method can do cheap exact comparisons on these canonical forms rather than more costly inexact comparisons. This technique is most appropriate for <em>immutable</em> classes; if the object can change, you must keep the canonical form up-to-date. </p>
  </blockquote>
  
  <p>So my question (and I double-checked what 'canonical' means): what is Bloch talking about? What would the canonical form be? I'm ready to be told that the answer is very simple (presumably otherwise his editor would have told him to add more) but I want to see other people say so. </p>
  
  <p>He also mentions the same thing for <code>hashCode()</code> in the next item 9.</p>
  
  <p>To give it in context, he also discusses a bad version of the <code>equals()</code> method for <code>CaseInsensitiveString</code>:</p>
  
  <pre><code>// Broken - violates symmetry
  @Override public boolean equals(Object o) {
      if (o instanceof CaseInsensitiveString)
          return s.equalsIgnoreCase(
              ((CaseInsensitiveString) o).s);
      if (o instanceof String) // one-way interoperability!
          return s.equalsIgnoreCase((String) o);
      return false;
  }
  </code></pre>