簡體   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