2012-07-15 160 views
42

我有一個這樣的通用接口:如何用泛型實現枚舉?

interface A<T> { 
    T getValue(); 
} 

此接口已經有限的情況下,因此這將是最好的實現他們爲枚舉值。問題是這些實例有不同類型的值,所以我嘗試了以下方法,但它不能編譯:

public enum B implements A { 
    A1<String> { 
     @Override 
     public String getValue() { 
      return "value"; 
     } 
    }, 
    A2<Integer> { 
     @Override 
     public Integer getValue() { 
      return 0; 
     } 
    }; 
} 

對此有何想法?

回答

42

你不行。 Java不允許在枚舉常量上使用泛型類型。他們被允許在枚舉類型,但:

public enum B implements A<String> { 
    A1, A2; 
} 

,你可以在這種情況下,做的是要麼爲每個泛型類型枚舉類型,或只是使其成爲一類「假」有一個枚舉:

public class B<T> implements A<T> { 
    public static final B<String> A1 = new B<String>(); 
    public static final B<Integer> A2 = new B<Integer>(); 
    private B() {}; 
} 

不幸的是,他們都有缺點。

+1

這種方法不解決我的問題。 – 2012-07-15 08:28:49

+6

這就是我試圖做的 - 你的問題不能通過使用一個'Enum'來解決。 – Jorn 2012-07-15 08:34:45

+0

現在至少我明白'枚舉'不適合我的要求。謝謝! – 2012-07-15 08:39:24

24

作爲Java開發人員設計某些API,我們遇到這個問題頻繁。我再次確認我自己的疑惑,當我遇到這個帖子來了,但我有一個詳細的解決方法吧:

// class name is awful for this example, but it will make more sense if you 
// read further 
public interface MetaDataKey<T extends Serializable> extends Serializable 
{ 
    T getValue(); 
} 

public final class TypeSafeKeys 
{ 
    static enum StringKeys implements MetaDataKey<String> 
    { 
     A1("key1"); 

     private final String value; 

     StringKeys(String value) { this.value = value; } 

     @Override 
     public String getValue() { return value; } 
    } 

    static enum IntegerKeys implements MetaDataKey<Integer> 
    { 
     A2(0); 

     private final Integer value; 

     IntegerKeys (Integer value) { this.value = value; } 

     @Override 
     public Integer getValue() { return value; } 
    } 

    public static final MetaDataKey<String> A1 = StringKeys.A1; 
    public static final MetaDataKey<Integer> A2 = IntegerKeys.A2; 
} 

在這一點上,你獲得的是一個真正的恆enum關合作價值的利益(和所有的與此相伴的福利),以及interface的獨特實現,但您擁有enum所需的全局可訪問性。

顯然,這增加了冗長,這創造了複製/粘貼錯誤的可能性。您可以製作enum s public並簡單地爲其訪問添加一個額外的圖層。

傾向於使用這些功能的設計往往會遭受脆弱的實現,因爲它們通常會與其他一些獨特的值(如名稱)耦合在一起,這些值可能無意中在代碼庫中重複出現,以實現類似但不同的目的。通過全面使用enum,平等是免費的免費的脆弱行爲。

的主要缺點,例如系統,超越冗長,是來回轉換全局唯一鍵之間(例如,編組和從JSON)的想法。如果他們只是鑰匙,那麼他們可以以浪費內存爲代價安全地重新實施(重複),但使用之前的弱點 - 優勢在於 - equals

有一種變通方法,以這種通過與每全局實例匿名類型塞滿它提供了全球範圍內實施的獨特性:

public abstract class BasicMetaDataKey<T extends Serializable> 
    implements MetaDataKey<T> 
{ 
    private final T value; 

    public BasicMetaDataKey(T value) 
    { 
     this.value = value; 
    } 

    @Override 
    public T getValue() 
    { 
     return value; 
    } 

    // @Override equals 
    // @Override hashCode 
} 

public final class TypeSafeKeys 
{ 
    public static final MetaDataKey<String> A1 = 
     new BasicMetaDataKey<String>("value") {}; 
    public static final MetaDataKey<Integer> A2 = 
     new BasicMetaDataKey<Integer>(0) {}; 
} 

注意,每個實例使用匿名執行,但沒有別的,是落實它需要,所以{}是空的。這既令人困惑又令人討厭,但它適用於實例引用更可取並且雜亂度保持最低的情況,儘管對於經驗較少的Java開發人員來說可能有點神祕,因此難以維護。

最後,提供全球唯一性和重新分配的唯一方法是對所發生的事情稍微有點創意。對於全球共享接口,最常見的用法,我已經看到是傾向於混合了很多不同的價值觀,不同類型的元數據桶(在T,在每個鍵基礎上):

public interface MetaDataKey<T extends Serializable> extends Serializable 
{ 
    Class<T> getType(); 
    String getName(); 
} 

public final class TypeSafeKeys 
{ 
    public static enum StringKeys implements MetaDataKey<String> 
    { 
     A1; 

     @Override 
     public Class<String> getType() { return String.class; } 

     @Override 
     public String getName() 
     { 
      return getDeclaringClass().getName() + "." + name(); 
     } 
    } 

    public static enum IntegerKeys implements MetaDataKey<Integer> 
    { 
     A2; 

     @Override 
     public Class<Integer> getType() { return Integer.class; } 

     @Override 
     public String getName() 
     { 
      return getDeclaringClass().getName() + "." + name(); 
     } 
    } 

    public static final MetaDataKey<String> A1 = StringKeys.A1; 
    public static final MetaDataKey<Integer> A2 = IntegerKeys.A2; 
} 

這提供了與第一種選擇相同的靈活性,並且它提供了一種通過反射獲得參考的機制(如果稍後有必要的話),因此避免了稍後需要實例化。它還避免了第一個選項提供的大量容易出錯的複製/粘貼錯誤,因爲如果第一個方法錯誤,它將不會編譯,而第二個方法不需要更改。唯一要注意的是,你應該確保enum命中註定該方式使用是public避免讓任何人訪問錯誤,因爲他們沒有進入內enum;如果你不想擁有這些MetaDataKey回事跨越編組線,然後讓他們隱藏的來自外部的包可用於自動丟棄(編組時,反射性地檢查,看看是否enum是可訪問的,如果不是,然後忽略鍵/值)。除非提供兩種訪問實例的方法,否則沒有任何獲取或丟失,除非提供了更明顯的static引用(因爲無論如何,enum實例就是這樣)。

我只是希望他們把它使enum小號可以用Java擴展對象。也許在Java 9中?

最後的選擇並不能真正解決您的需求,爲你問的價值,但我懷疑這對獲取的實際目標。除非我用`枚舉乙implemts一個',這反過來又使通用接口無意義

  0

非常感謝分享此解決方法!其實你不需要使用'enum',只需使用匿名內部類,這正是我現在所做的。Zhao Yi 22 5月. 13

+1

沒問題。匿名的內部類是中間的例子。我傾向於避免使用匿名類來維護可維護性(編譯後的代碼變得與'$ 1','2'類匿名類型混淆,許多開發人員會錯過匿名類型的原因),但這絕對是一種有效的和更小的方法。pickypg 22 5月. 13

  0

我真的很感激這個答案。感謝你的付出。Christian Conti-Vock 01 11月. 17

  0

這是一篇舊帖子,但您能否總結您提供的實施的成本和收益?我很難理解爲什麼我會選擇其中一個。Matthew Phipps 16 1月. 18


+0

非常感謝分享此解決方法!其實你不需要使用'enum',只需使用匿名內部類,這正是我現在所做的。 – 2013-05-22 06:12:07

+1

沒問題。匿名的內部類是中間的例子。我傾向於避免使用匿名類來維護可維護性(編譯後的代碼變得與'$ 1','2'類匿名類型混淆,許多開發人員會錯過匿名類型的原因),但這絕對是一種有效的和更小的方法。 – pickypg 2013-05-22 06:42:12

+0

我真的很感激這個答案。感謝你的付出。 – 2017-11-01 16:22:13