I have a package private interface that's able to identify equality based on captured synthetic argument references.
interface SynthEqual {
default Object synthParamAt(int paramIndex) {
try {
return getClass().getDeclaredFields()[paramIndex].get(this);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
default<S> boolean equalTo(int paramIndex, S other) {
return synthParamAt(paramIndex).equals(other);
}
//Edit:
// I realized this is a better option, instead of exposing the synthetic param:
default boolean equalTo(int at, SynthEqual that) {
return that != null && that.synthParamAt(at).equal(synthParamAt(at));
}
// and making synthParamAt private with Java9
}
This interface is used for when lambda captures more than 2 arguemnts, but I am interested only on the equality of one of them...
If you .hash this synthetic arguments they are different... EVEN IF they come from the same reference source.
But if you == Or .equal() the result comes as true.
This is why I rather NOT @Override equals or hashcode, instead I always use my plain custom equalizer methods..., BUT if you wish/need... you could... in theory do this:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AClass<?> that = (AClass<?>) o;
return that.lambdaFieldA.equalTo(0, this.lambdaFieldA.synthParamAt(0)) && Objects.equals(fieldB, that.fieldB);
}
As you can see this composed class is properly overriding equals, but my lambda is using it's custom synthetic equality test.
as for hashCode... well I don't know how would that be overridden... I believe it is not possible to override hash in such a way that synthetic arguments can be inferred. Up to now, this has never been an issue...
The real problem comes when dealing with Maps.
When we look at the putVal() method of ConcurrenthashMap we can see that they are using the hash function to search for a match in the Node table.
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
//Here
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh; K fk; V fv;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
// Here the fetch
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
// .......
// A few lines bellow:
else if (onlyIfAbsent // check first node without acquiring lock
//Here compares
&& fh == hash
&& ((fk = f.key) == key || (fk != null && key.equals(fk))) // FINALLY!!!
&& (fv = f.val) != null)
return fv;
.....
static final int spread(int h) {
return (h ^ (h >>> 16)) & HASH_BITS;
}
I can see, that .equals is performed ONLY After a hash search has found a match... So I believe I need to fix my hash override for my equals to be able to test.
How could I override the K key in such a way that it properly deals with synthetic argument equalities under the rules of a ConcurrentHashMap?