如何在java枚举中重写(final)equals方法?
我在覆盖枚举中的equals方法以使其与其他类兼容时遇到问题。 Enum实现了一个接口,其思想是可以对该接口的所有实现进行平等性测试,而不管它们的类型如何。例如:如何在java枚举中重写(final)equals方法?,java,enums,overriding,equals,final,Java,Enums,Overriding,Equals,Final,我在覆盖枚举中的equals方法以使其与其他类兼容时遇到问题。 Enum实现了一个接口,其思想是可以对该接口的所有实现进行平等性测试,而不管它们的类型如何。例如: public interface Group { public Point[] getCoordinates(); } public enum BasicGroups implements Group { a,b,c; // simplified, they actually have constructors
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
}
如果BasicGroup
和OtherGroup
具有相同的坐标(以任意顺序),则equals方法应返回true
执行myOtherGroup.equals(BasicGroup.a)
时没有问题,但是由于Enums中的equals方法是final,因此我无法重写它们
有办法解决这个问题吗?与在另一个BasicGroup上测试时一样,使用默认的equals方法(引用相等),在测试其他类时使用我自己的实现。当我使用
BasicGroup.a.equals(myOtherGroup)
时,如何确保java不会使用错误的方法?在java中不可能这样做。(当谈到方法时,final关键字的唯一目的是防止重写!)
equals
和枚举上的其他一些方法是final,因此您无法更改它们的行为。(你不应该:)以下是我对一个问题的答案:
处理枚举常量的客户端的直觉是,两个常量
相等
当且仅当它们是相同的常量时。因此,除了返回this==other
之外的任何其他实现都会违反直觉且容易出错
同样的推理也适用于hashCode()
,clone()
,compareTo(Object)
,name()
,ordinal()
,以及getDeclaringClass()
JLS并没有激发最终选择的动机,但在枚举的上下文中提到了equals。片段:
Enum中的equals方法是最后一个方法,它仅对其参数调用super.equals并返回结果,从而执行标识比较
您可以通过调用方法HassameCoordinates或类似方法而不是equals来解决此问题 Enum的equals在语言规范中定义,因此您不能希望重新定义它。您不能
@重写afinal
方法();这一点很清楚<代码>枚举
类型()在Java中得到了非常特殊的处理,这就是为什么equals
是最终的
(还有克隆
,哈希代码
,等等)根本不可能@重写枚举
的equals
方法,在更典型的使用场景中也不可能
但是,从全局来看,您似乎正在尝试遵循有效Java第二版第34项:使用接口模拟可扩展枚举中推荐的模式(有关enum
的更多信息,请参阅):
您已经定义了此接口
(现在明确记录了预期的等于
行为):
当然,接口
定义实现者的equals
方法的行为方式是完全可以接受的。这正是,例如。空的链接列表
等于空的数组列表
,反之亦然,因为接口
要求这样做
在您的例子中,您选择将一些组
实现为枚举
。不幸的是,您现在无法按照规范实现equals
,因为它是最终的
,您无法@覆盖它。但是,由于目标是遵守组
类型,您可以通过使用转发组
来使用,如下所示:
public class ForwardingGroup implements Group {
final Group delegate;
public ForwardingGroup(Group delegate) { this.delegate = delegate; }
@Override public Point[] getCoordinates() {
return delegate.getCoordinates();
}
@Override public boolean equals(Object o) {
return ....; // insert your equals logic here!
}
}
现在,您不用将枚举
常量直接用作组
,而是将它们包装在转发组
的实例中。现在,此组
对象将具有所需的等于
行为,如接口
所指定
也就是说,而不是:
// before: using enum directly, equals doesn't behave as expected
Group g = BasicGroup.A;
您现在有了类似于:
// after: using decorated enum constants for proper equals behavior
Group g = new ForwardingGroup(BasicGroup.A);
附加说明
enum BasicGroups实现了Group
,尽管它本身并不遵循Group.equals
的规范,但这一事实应该被清楚地记录下来。必须提醒用户,常数必须包装在ForwardingGroup
中,以实现正确的equals
行为
还请注意,您可以缓存ForwardingGroup
的实例,每个enum
常量对应一个实例。这将有助于减少创建的对象数量。根据有效的java第二版,项目1:考虑静态工厂方法而不是构造函数,可以考虑使用<代码>转发组>代码>定义一个代码>静态GETSimple(组G)< /C>方法,而不是一个构造函数,允许它返回缓存实例。
我假设Group
是一种不可变类型(有效的Java第二版,第15项:最小化可变性),否则您可能不应该首先使用enum
实现它。鉴于此,考虑有效的java第二版,项目25:喜欢列表到数组。您可以选择让getCoordinates()
返回列表,而不是点[]
。您可以使用(另一个decorator!),这将使返回的列表不可变。相比之下,由于数组是可变的,因此在返回点[]
时,您将被迫执行防御性复制
另见
平等是相当难以捉摸的。不同的语境需要不同的平等关系。通过在对象上使用equals()方法,Java强加了一个“内在”等式,API和Set一样依赖于它
同时,排序并不被认为是“内在的”,两个对象可以在不同的上下文中进行不同的排序,API通常允许我们提供一个压缩机,即自定义排序关系
这是
// after: using decorated enum constants for proper equals behavior
Group g = new ForwardingGroup(BasicGroup.A);
interface Equalator
boolean equal(a, b)
public HashSet( Equalator equalator )