繁体   English   中英

如何覆盖java枚举中的(final)equals方法?

[英]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
}

如果BasicGroupOtherGroup具有相同的坐标(按任意顺序),则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中被非常特殊地处理,这就是为什么equalsfinal (也就是clonehashCode等)。它根本不可能@Override enumequals方法,也不是你真的想要的更典型的使用场景。

但是 ,从整体来看,看起来您正在尝试遵循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 equalsArrayList ,反之亦然,因为这是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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM