neX*_*Xus 14 java enums overriding final equals
我在覆盖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
}
Run Code Online (Sandbox Code Playgroud)
如果a BasicGroup和a OtherGroup具有相同的坐标(按任意顺序),则equals方法应返回true.
执行时没问题,myOtherGroup.equals(BasicGroup.a)但由于Enums中的equals方法是最终的,我无法覆盖它们.
有办法解决这个问题吗?就像在另一个BasicGroup上测试时一样,使用默认的equals方法(引用相等),并且在测试其他类时使用我自己的实现.我该如何确保java不使用错误的BasicGroup.a.equals(myOtherGroup)?
pol*_*nts 17
你不能用 @Override一种final方法(§8.4.3.3); 这很清楚.enum类型(§8.9)在Java中处理得非常特别,这就是为什么equals是final(也是clone,hashCode等),这是根本不可能@Override的equals的方法enum,也不是你真的想在一个更典型的使用场景.
但是,从整体上看,看起来您正在尝试遵循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);
}
Run Code Online (Sandbox Code Playgroud)
当然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!
}
}
Run Code Online (Sandbox Code Playgroud)
现在,而不是用enum直接常量Group,你包裹他们的一个实例ForwardingGroup.现在,此Group对象将具有所需的equals行为,如interface.
也就是说,而不是:
// before: using enum directly, equals doesn't behave as expected
Group g = BasicGroup.A;
Run Code Online (Sandbox Code Playgroud)
你现在有类似的东西:
// after: using decorated enum constants for proper equals behavior
Group g = new ForwardingGroup(BasicGroup.A);
Run Code Online (Sandbox Code Playgroud)
事实上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()返回a List<Point>而不是Point[].你可以使用Collections.unmodifiableList(另一个装饰器!),这将使返回的List不可变.相比之下,由于数组是可变的,因此在返回时你将被迫执行防御性复制Point[].
在Java中不可能这样做.(当涉及到方法时,final关键字的唯一目的是防止覆盖!)
equalsEnums上的一些其他方法是最终的,所以你不能改变它们的行为.(你不应该 :)这是我对相关问题的回答:
处理枚举常量的客户端的直觉是,equal当且仅当它们是相同的常量时,两个常量才是.因此,除了return this == other违反直觉和容易出错之外的任何其他实现.
同样的道理也适用于hashCode(),clone(),compareTo(Object),name(),ordinal(),和getDeclaringClass().
JLS并没有激励选择让它成为最终版本,但在这里的枚举语中提到了相同的内容.片段:
Enum中的equals方法是一个最终方法,它只在其参数上调用super.equals并返回结果,从而执行身份比较.