I have a problem with overriding the equals method in an Enum to make it compatible with other classes.
The Enum implements an interface and the idea is that all implementations of this interface can be tested for equality, regardless of their type. For Example:
public interface Group {
public Point[] getCoordinates();
}
public enum BasicGroups implements Group {
a,b,c; // simplified, they actually have constructors
// + fields and methods
}
public class OtherGroup implements Group {
// fields and methods
}
If both a BasicGroup and an OtherGroup have the same coordinates (in arbitrary order) then the equals method should return true.
No problem when performing myOtherGroup.equals(BasicGroup.a) but since the equals method in Enums is final, I can’t override them.
Is there some way to work around this? Like when testing on another BasicGroup the default equals method (reference equality) is used and when testing other classes my own implementation is used. And how do I make sure that java doesn’t use the wrong one when I do BasicGroup.a.equals(myOtherGroup)?
You can NOT
@Overrideafinalmethod (§8.4.3.3); this much is clear.enumtypes (§8.9) are treated very specially in Java, which is why theequalsisfinal(alsoclone,hashCode, etc.) It’s simply not possible to@Overridetheequalsmethod of anenum, nor would you really want to in a more typical usage scenario.HOWEVER, looking at the big picture, it looks like you are trying to follow the pattern recommended in Effective Java 2nd Edition, Item 34: Emulate extensible enums with interfaces (see the language guide for more information about
enum):You have defined this
interface(now documented explicitly for expectedequalsbehavior):It is perfectly acceptable for an
interfaceto define howequalsmethod for implementors should behave, of course. This is exactly the case with, e.g.List.equals. An emptyLinkedListisequalsto an emptyArrayListand vice versa, because that’s what theinterfacemandates.In your case, you’ve chosen to implement some
Groupasenum. Unfortunately you now can’t implementequalsas per the specification, since it’sfinaland you can’t@Overrideit. However, since the objective is to comply to theGrouptype, you can use decorator pattern by having aForwardingGroupas follows:Now, instead of using your
enumconstants directly asGroup, you wrap them in an instance of aForwardingGroup. Now thisGroupobject will have the desiredequalsbehavior, as specified by theinterface.That is, instead of:
You now have something like:
Additional notes
The fact that
enum BasicGroups implements Group, even though it does not itself follow the specification ofGroup.equals, should be very clearly documented. Users must be warned that constants must be e.g. wrapped inside aForwardingGroupfor properequalsbehavior.Note also that you can cache instances of
ForwardingGroup, one for eachenumconstants. This will help reduce the number of objects created. As per Effective Java 2nd Edition, Item 1: Consider static factory methods instead of constructors, you may consider havingForwardingGroupdefine astatic getInstance(Group g)method instead of a constructor, allowing it to return cached instances.I’m assuming that
Groupis an immutable type (Effective Java 2nd Edition, Item 15: Minimize mutability), or else you probably shouldn’t implement it withenumin the first place. Given that, consider Effective Java 2nd Edition, Item 25: Prefer lists to arrays. You may choose to havegetCoordinates()return aList<Point>instead ofPoint[]. You can useCollections.unmodifiableList(another decorator!), which will make the returnedListimmutable. By contrast, since arrays are mutable, you’d be forced to perform defensive copying when returning aPoint[].See also
com.google.common.collect.ForwardingObject