<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body>
    <div class="moz-cite-prefix">On 1/17/2026 5:46 PM, Brian Goetz
      wrote:<br>
    </div>
    <blockquote type="cite" cite="mid:46bd050a-4d6a-41fe-a663-a0c5cd77c766@oracle.com">
      <blockquote type="cite" cite="mid:1085557496.17567763.1768646213005.JavaMail.zimbra@univ-eiffel.fr">
        <div style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000">
          <div>With a mutable class with equals/hashCode/toString
            generated, it's too easy to store an object in a collection,
            mutate it, and then never been able to find it again.</div>
        </div>
      </blockquote>
      <br>
      Yes, but also: everyone here knows about this risk.  You don't
      need to belabor the example :)<br>
      <br>
      This is a reflection of a problem we already have: equals is a
      semantic part of the type's definition, about when two instances
      represent the "same" value, and mutability is pat of the type's
      definition, and "whether you put it in a hash-based collection and
      then mutate it" is about _how the instances are used by
      clients_.  <br>
      <br>
      While immutability is a good default, its not always _wrong_ to
      use mutability; its just riskier.  And for a mutable class,
      state-based equality is _still_ a sensible possible implementation
      of equality; its just riskier.  And putting mutable objects in
      hash-based collections is also not wrong; its just riskier.  For
      the bad thing to happen, all of these have to happen _and then it
      has to be mutated_.  But if we have to assign primary blame here,
      it is not the guy who didn't write `final` on the fields, and not
      the guy who said that equality was state-based, but the guy who
      put it in the collection and mutated it.  <br>
      <br>
      If we decided that avoiding this risk were the primary design
      goal, then we would have to either disallow mutable fields, or
      change the way we define the default equals/hashCode behavior. 
      Potentially ways to do the latter include:<br>
      <br>
       - never provide a default implementation, inherit the object
      default<br>
       - don't provide a default implementation if there are any mutable
      fields<br>
       - leave mutable fields out of the default implementation, but use
      the other fields<br>
      <br>
      While "disallow mutable fields" is a potentially principled
      answer, it is pretty restrictive.  Of the others, I claim that the
      proposed behavior is better than any of them.  <br>
      <br>
      Carrier classes are about data, and come with a semantic claim:
      that the state description is a complete, canonical description of
      the state.  It seems pretty questionable then to use identity
      equality for such a class.  But the other two alternatives listed
      are both some form of "action at a distance", harder to keep track
      of, are still only guesses at what the user actually wants.  The
      two principled options are "don't provide equals/hashCode", and
      "state-based equals/hashCode", and of the two, the latter makes
      much more sense.<br>
    </blockquote>
    <p>What about "state-based equals with final fields-based hashCode"?
      (Maybe this is actually what you meant with `leave mutable fields
      out of the default implementation, but use the other fields`, but
      then I don't understand how that's "action at a distance" and
      "harder to keep track of".) That would solve the HashSet issue and
      be a safe, intuitive default. There might be performance issues
      for carrier classes without final fields that are used in large
      HashSets, but in that case it's easy enough to provide one's own
      implementation of `hashCode`. And by doing so, one would
      implicitly consent to the implications of doing so (I could
      imagine javac issuing a lint warning for this and/or javadoc
      adding a warning to the Javadoc that the carrier class suffers
      from the HashSet issue).</p>
    <p>Kind regards, Anthony</p>
    <blockquote type="cite" cite="mid:46bd050a-4d6a-41fe-a663-a0c5cd77c766@oracle.com">It is
      not a bug to put a mutable object in a HashSet; it is a bug to do
      that _and_ to later mutate it.  So detuning the semantics of
      carriers, from something simple and principled to something
      complicated and which is just a guess about what the user really
      wants, just because someone might do two things that are each
      individually OK but together not OK, seems like an
      over-rotation.  <br>
      <br>
    </blockquote>
  </body>
</html>