If we expect values to “work like an int”, then |==| comparisons on value types should work like they do on primitives:

|value class Point { ... } ... client code ... /* mutable */ Point location; void setLocation(Point p) { if (p != location) { fireExpensivePropertyChangeEvent(location, p); this.location = p; } } |

Users will reasonably expect this idiom to work, just as they would for |int| properties.

There have been at least four suggested treatments of |val==|:

 * Illegal; compilation error
 * Always false; user must follow up with |.equals()| test
 * Substitutibility test — are these the same value
 * Delegate to |.equals()| (call this “deep substitutibility”)

I think the first is unreasonable and should be discarded, based on the above illustration of failure to “work like an int”; I think the second is the same.

We expect generic code to use the LIFE idiom:

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

The LIFE idiom arose because |ref==| is fast, and so we can optimize away the potentially expensive |.equals()| call when called with identical objects. But, if the |val==| test becomes more expensive, then what started out as an optimization, might lead to duplication of nontrivial work, because the |.equals()| implementation may well also have an |==| test. But, we don’t want to let the optimization tail wag the dog here.

Separately, we have been reluctant to push value substitutibility into |acmp|, for fear of perturbing the performance model there. So, let’s posit a |vcmp|, a sibling to |{ildf}cmp|, and translate |val==| to that, and let |acmp| continue to return false when either operand is a value.

Now, for pure value code, like the above, we translate |val==| to |vcmp|, and we work like an int. And for generic code, the LIFE idiom:

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

(when |x| and |y| are of type |T|) translates to an |acmp| backed up by an |invokevirtual|, and when |T| is specialized to a value, the |acmp| is fast and always false, and when specialized to a reference, is fast and computes |ref==|. I believe this gives everyone what they want:

 * value equality is intuitive and appropriately fast
 * Existing generics continue using LIFE, and get fast (enough)
   translation both for reference and value instantiations
 * Generic code that forgets |.equals()| is equally wrong for reference
   and value instantiations
 * Generic code that goes straight to |.equals()| works correctly for
   reference and value instantiation

​

Reply via email to