Expected behavior
Object#equals's contract requires:
It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
Observed/Actual behavior
Equivalence between TypedKeyImpl and KeyImpl is not symmetric
Steps/models to reproduce
Key key = Key.key("minecraft:stone");
TypedKey<ItemType> typedKey = TypedKey.create(RegistryKey.ITEM, key);
System.out.println(key.equals(typedKey)); // true
System.out.println(typedKey.equals(key)); // false
Plugin and Datapack List
None
Paper version
This server is running Paper version 1.21.11-126-main@3f5728e (2026-02-28T11:49:07Z) (Implementing API version 1.21.11-R0.1-SNAPSHOT)
Other
In my opinion TypedKey shouldn't extend Key. But since it is too late to change this now, I think the problem could be solved by:
- In
TypedKeyImpl, override equals and check if the other object is also a TypedKey. If so, additionally compare registry key. Otherwise, only compare namespace and value.
- In
TypedKeyImpl, override hashCode and replace its implementation with that of KeyImpl (from adventure), such that the registry key has no influence on the hash code and equal keys have equal hash codes.
Expected behavior
Object#equals's contract requires:Observed/Actual behavior
Equivalence between
TypedKeyImplandKeyImplis not symmetricSteps/models to reproduce
Plugin and Datapack List
None
Paper version
This server is running Paper version 1.21.11-126-main@3f5728e (2026-02-28T11:49:07Z) (Implementing API version 1.21.11-R0.1-SNAPSHOT)
Other
In my opinion
TypedKeyshouldn't extendKey. But since it is too late to change this now, I think the problem could be solved by:TypedKeyImpl, overrideequalsand check if the other object is also aTypedKey. If so, additionally compare registry key. Otherwise, only compare namespace and value.TypedKeyImpl, overridehashCodeand replace its implementation with that ofKeyImpl(from adventure), such that the registry key has no influence on the hash code and equal keys have equal hash codes.