> Unfortunately there's no halfway in this; it has to be the ONLY way,
> even for comparisons between 2 instances of the same subtype. Thus, if
> one creates a coloured list, which adds a color property to a list,
> then a red list containing "a", "b", and "c" has to be entirely equal
> to a blue list containing "a", "b", and "c", and even produce the same
> hashCode, or it won't work. This is severely troublesome, as it is not
> possible to convey the notion that subclasses cannot add state like
> this, except in documentation. Nevertheless, there are legitimate
> reasons to do this. java.util.List is exhibit A for this model.
I can't imagine any class implementing List which really needs
additional state, but I know, there are too many things I can't imagine.
However, I'm quite sure, there's a way to implement a correct equals
even for lists with colored subclasses, so that a red is unequal to the
blue one, should I try to describe it?
> The most pragmatic implementation of such an 'equivalence level'
> notion Roel and I came up with involves 2 changes to how equals/
> hashCode is currently generated by lombok:
>
> A. instead of using other.getClass() == this.getClass(), use other
> instanceof MyType.
As already discussed here, this is the way to go when dynamic proxies
get used.
> B. make equals and hashCode final.
This should be independent of A, shouldn't it? Actually, it's fine even
for dynamic proxies (unless they think they must override it), and I
don't see any case where just one of A and B is needed.
> [C. optional: add javadoc explaining the vagaries of what it means to
> work with equivalence reference point style equality.]
Note, that it's not possible to automatically generate EqAHC for List or
any similar class coming to my mind.
> "I can think of 50 other obscure little changes one might wish for in
> rare and very specific circumstances to the way lombok generates
> things. Why are those not also deserving of an option?"
>
> If we can't answer that question, the feature won't go in, because as
> good as it might sound, having 50 different parameters on @Data or
> @EqualsAndHashCode is clearly inferior to the lombok of today.
Sure, and setting all the options could in fact increase boilerplate
since it could get longer than the generated code. :D
> Having said all that, supporting the equivalence reference point
> notion is something that really does seem worthy of an option. Though,
> far more likely we'd use an alternate annotation, which is mutually
> exclusive with @EqualsAndHashCode. In retrospect, @EqAHC was a bad
> name. @EquivalenceRelation or @FieldBasedEquivalence or some such
> would have been far smarter.
I disagree.
Aren't mutually exclusive annotations the more complicated thing? Aren't
they going to use about the same parameters?
The annotated class is no EquivalenceRelation at all (you just define
one on its instances), so it shouldn't be annotated this way.
With using getters by default, you just can't call it
FieldBasedEquivalence. Something like PropertyBasedEquivalence would be
better, but it gets too long. This and the fact that the annotated class
is no equivalence makes me happy with EaHC again.
I'd also use preferFields or ignoreGetters instead of doNotUseGetters,
but this doesn't matter and comes too late, anyway.
It will not. You're assuming that the actual class and all additional
attributes get ignored in the base class equals, but this is not the way
to go.
> Therefore a blue empty list and a red empty
> list must be equals() to each other, or you break the contract.
Using your assumption, yes. In case I'm right, no.
> Using instanceof for dynamic proxies introduces a hard-to-dodge
> opportunity for folks to break the equals/hashCode contract by
> subclassing and adding state of any sort, and/or overriding equals/
> hashCode. Therefore, using instanceof instead of .getClass() must go
> hand in hand with making hashCode and equals final,
Not necessarily. Let me cite you:
Programming is turing-complete, which means shooting yourself in the
foot is always an option. You can't prevent folks from doing it. You can
only make nice APIs and document them appropriately.
Making EaHC final may be fine for dynamic proxies and may prevent some
errors. Making equals work *with* additional state in subclasses
requires overriding.
Have a look at my example at
http://dl.dropbox.com/u/4971686/lombok/EqualsDemo1.java
There's still a problem, I've got a solution, but I'm looking for a
simpler one.
> *and* we need to
> document that state relevant for equality can no longer be added.
> Yes, FieldBasedEquivalance isn't really the right name either, though,
> while we use getters, all lombok annotations are currently very much
> field based. The presence of a getter doesn't mean we'll call it. We
> look for a field, and use it, though we'll access it via getX()
> instead of x if you also have a getter for it.
That's new to me and not really obvious from the documentation. I could
find it out when reading the javadoc for EqualsAndHashCode.of and
thinking hard....
> If we ever do tackle this notion (as I mentioned, due to its rarity
> its low on the list), we'll think it through properly :P
IMHO, an equals compatible with dynamic proxies is a very commonly
needed thing. Agreed, anything beyond that is rare.