[英]How to override the (final) equals method in java enums?
我在覆盖Enum中的equals方法时遇到问题,使其与其他类兼容。 Enum实现了一个接口,其思路是可以测试此接口的所有实现是否相等,无论其类型如何。 例如:
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不使用错误的?
你不能 @Override
一个final
方法( §8.4.3.3 ); 这很清楚。 enum
类型(第8.9节 )在Java中被非常特殊地处理,这就是为什么equals
是final
(也就是clone
, hashCode
等)。它根本不可能@Override
enum
的equals
方法,也不是你真的想要的更典型的使用场景。
但是 ,从整体来看,看起来您正在尝试遵循Effective Java 2nd Edition中建议的模式,第34项:使用接口模拟可扩展枚举 (有关enum
更多信息,请参阅语言指南 ):
您已定义此interface
(现在已明确记录了预期的equals
行为):
public interface Group implements Group {
public Point[] getCoordinates();
/*
* Compares the specified object with this Group for equality. Returns true
* if and only if the specified object is also a Group with exactly the same
* coordinates
*/
@Override public boolean equals(Object o);
}
当然, interface
定义实现者的equals
方法应该如何表现是完全可以接受的。 这正是例如List.equals
的情况。 空LinkedList
equals
空ArrayList
,反之亦然,因为这是interface
要求的。
在您的情况下,您已选择将某个Group
实现为enum
。 不幸的是,你现在不能按照规范实现equals
,因为它是final
,你不能@Override
它。 但是,由于目标是遵守Group
类型 ,因此可以通过具有ForwardingGroup
来使用装饰器模式 ,如下所示:
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!
}
}
现在,不是将enum
常量直接用作Group
,而是将它们包装在ForwardingGroup
的实例中。 现在,此Group
对象将具有所需的equals
行为,如interface
所指定。
也就是说,而不是:
// 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 implements Group
的事实,即使它本身不遵循Group.equals
的规范,也应该非常清楚地记录 。 必须警告用户必须将常量包装在ForwardingGroup
以获得正确的equals
行为。
另请注意,您可以缓存ForwardingGroup
实例,每个enum
常量一个。 这有助于减少创建的对象数量。 根据Effective Java 2nd Edition,第1项:考虑静态工厂方法而不是构造函数 ,您可以考虑让ForwardingGroup
定义static getInstance(Group g)
方法而不是构造函数,允许它返回缓存实例。
我假设Group
是一个不可变类型( Effective Java 2nd Edition,Item 15:Minimize mutability ),否则你可能不应该首先使用enum
实现它。 鉴于此,请考虑Effective Java 2nd Edition,Item 25:Prefer list to arrays 。 您可以选择让getCoordinates()
返回List<Point>
而不是Point[]
。 您可以使用Collections.unmodifiableList
(另一个装饰器!),这将使返回的List
不可变。 相比之下,由于数组是可变的,因此在返回Point[]
时,您将被迫执行防御性复制。
在Java中不可能这样做。 (当涉及到方法时,final关键字的唯一目的是防止覆盖!)
Enums上的equals
和一些其他方法是最终的,因此您无法更改它们的行为。 (你不应该 :)这是我对相关问题的回答 :
处理枚举常量的客户端的直觉是,当且仅当它们是相同的常量时,两个常量是equal
。 因此,除了return this == other
任何其他实现将是违反直觉和容易出错的。
相同的推理适用于hashCode()
, clone()
, compareTo(Object)
, name()
, ordinal()
和getDeclaringClass()
。
JLS并没有激励选择让它成为最终版本,但在这里的枚举语中提到了相同的内容。 片段:
Enum中的equals方法是一个最终方法,它只在其参数上调用super.equals并返回结果,从而执行身份比较。
您可以通过调用方法hasSameCoordinatesAs或类似方法而不是equals来解决此问题。
enums的equals在语言规范中定义,因此您无法重新定义它。
平等是相当难以捉摸的。 不同的背景需要不同的平等关系。 通过在Object上使用equals()方法,Java强加了“内在”相等性,而像Set这样的API依赖于它。
同时,排序不被视为“内在的”,两个对象可以在不同的上下文中以不同的方式排序,并且API通常允许我们提供comprator,即自定义排序关系。
这是有趣的。 在数学术语中,平等与秩序一样,只是一种关系,并且可以存在不同的平等关系。 “内在平等”的概念并不神圣。
所以我们也有一个Equal-ator,并更改API以接受自定义的平等关系:
interface Equalator
boolean equal(a, b)
public HashSet( Equalator equalator )
实际上,我们可以围绕当前集合API构建包装器,并添加新的相等功能。
这可能会回答您的问题。 为什么你首先依赖于equals()? 你可以删除它,并依赖“equalator”吗? 然后你就定了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.