2012-07-15 15 views
42

मैं इस तरह एक सामान्य इंटरफ़ेस है:जेनिक्स के साथ enum कैसे कार्यान्वित करें?

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

यह इंटरफ़ेस सीमित है उदाहरणों, इसलिए यह उनके enum मूल्यों के रूप में लागू करने के लिए सबसे अच्छा होगा। समस्या उन उदाहरणों मूल्यों के विभिन्न प्रकार है, तो मैं निम्नलिखित दृष्टिकोण की कोशिश की लेकिन यह संकलन नहीं करता है:

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

इस बारे में कोई विचार?

उत्तर

42

आप नहीं कर सकते। जावा enum स्थिरांक पर सामान्य प्रकार की अनुमति नहीं देता है। वे, हालांकि enum प्रकार पर अनुमति दी जाती है:

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

आप इस मामले में क्या कर सकता है क्या या तो एक सामान्य प्रकार के लिए एक enum प्रकार, या 'झूठे' बस इसे एक वर्ग बनाकर एक enum होने है:

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

यह दृष्टिकोण मेरी समस्या का समाधान नहीं करता है जब तक कि मैं 'enum B implemts ए ' का उपयोग नहीं करता, जो बदले में सामान्य इंटरफ़ेस को अर्थहीन बनाता है। –

+6

यही वह बिंदु है जिसे मैं बनाने की कोशिश कर रहा हूं - आपकी समस्या को एकल 'एनम' का उपयोग करके हल नहीं किया जा सकता है। – Jorn

+0

अब कम से कम मैं समझता हूं कि 'enum' मेरी आवश्यकता के लिए इतना उपयुक्त नहीं है। धन्यवाद! –

24

जावा डेवलपर्स कुछ एपीआई डिजाइन करने के रूप में, हम अक्सर इस मुद्दे पर आते हैं। मैं अपने ही संदेह reconfirming रहा था जब मैं इस पोस्ट भर में आया था, लेकिन मैं यह करने के लिए एक वर्बोज़ वैकल्पिक हल है:

// 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 eration मूल्य जा रहा है का लाभ प्राप्त करता है (और के सभी भत्ते जो उसके साथ जाते हैं), साथ ही interface का अनूठा कार्यान्वयन भी है, लेकिन आपके पास enum द्वारा वैश्विक पहुंच योग्यता है।

स्पष्ट रूप से, यह शब्दशः जोड़ता है, जो कॉपी/पेस्ट गलतियों की संभावना बनाता है। आप enum s public बना सकते हैं और बस उनकी पहुंच में एक अतिरिक्त परत जोड़ सकते हैं।

डिजाइन कि इन सुविधाओं का इस्तेमाल करते हैं क्योंकि वे आम तौर इस तरह के एक नाम है, जो अनजाने एक समान है, फिर भी अलग उद्देश्य के लिए codebase भर में दोहराया जा सकता है, कुछ अन्य अनूठा मूल्य के साथ मिलकर कर रहे हैं भंगुर equals कार्यान्वयन से ग्रस्त हो जाते हैं। बोर्ड में enum एस का उपयोग करके, समानता एक फ्रीबी है जो इस तरह के भंगुर व्यवहार से प्रतिरक्षा है।

शब्दावली से परे प्रणाली जैसे प्रमुख दोष, वैश्विक स्तर पर अद्वितीय कुंजी (उदाहरण के लिए, जेएसओएन से मार्शलिंग) के बीच आगे और पीछे परिवर्तित करने का विचार है। यदि वे केवल चाबियाँ हैं, तो उन्हें स्मृति को बर्बाद करने की लागत पर सुरक्षित रूप से पुनर्स्थापित (डुप्लिकेट) किया जा सकता है, लेकिन पहले की कमजोरी का उपयोग करके - 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) {}; 
} 

ध्यान दें कि प्रत्येक उदाहरण के एक गुमनाम कार्यान्वयन का उपयोग करता है, लेकिन कुछ नहीं इसे लागू करने की जरूरत है , इसलिए {} खाली हैं। यह भ्रमित और परेशान दोनों है, लेकिन यह काम करता है अगर इंस्टेंस संदर्भ बेहतर हैं और अव्यवस्था को कम से कम रखा जाता है, हालांकि यह कम अनुभवी जावा डेवलपर्स के लिए थोड़ा सा गूढ़ हो सकता है, जिससे इसे बनाए रखना मुश्किल हो जाता है।

अंत में, वैश्विक विशिष्टता और पुन: असाइनमेंट प्रदान करने का एकमात्र तरीका यह हो रहा है कि क्या हो रहा है के साथ थोड़ा और रचनात्मक होना है।कि मैंने देखा है विश्व स्तर पर साझा इंटरफेस के लिए सबसे आम उपयोग मेटाडाटा बाल्टी कि विभिन्न प्रकार के साथ, विभिन्न मूल्यों का एक बहुत मिश्रण के लिए करते हैं के लिए कर रहे हैं (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 पहुंच योग्य है या नहीं, और यदि ऐसा नहीं है, फिर कुंजी/मान को अनदेखा करें)। यदि संदर्भों को बनाए रखा जाता है (उदाहरण के लिए enum उदाहरण वैसे भी हैं) उदाहरण के लिए दो तरीकों को छोड़कर public बनाकर प्राप्त या खो गया कुछ भी नहीं है।

मैं चाहता हूं कि उन्होंने इसे बनाया ताकि enum एस जावा में ऑब्जेक्ट्स का विस्तार कर सके। शायद जावा 9 में?

अंतिम विकल्प वास्तव में आपकी आवश्यकता को हल नहीं करता है, क्योंकि आप मूल्यों के लिए पूछ रहे थे, लेकिन मुझे संदेह है कि यह वास्तविक लक्ष्य की ओर जाता है।

+0

इस कामकाज को साझा करने के लिए बहुत बहुत धन्यवाद! दरअसल आपको 'enum' का उपयोग करने की आवश्यकता नहीं है, केवल अज्ञात आंतरिक कक्षाओं का उपयोग करें, जो मैं अब कर रहा हूं। –

+1

कोई समस्या नहीं है। अनाम आंतरिक कक्षा मध्य उदाहरण है। मैं रखरखाव के लिए गुमनाम वर्गों से बचने के लिए प्रवृत्त हूं (संकलित कोड '$ 1', '$ 2' गुमनाम प्रकारों के साथ भ्रमित हो जाता है और कई डेवलपर अज्ञात प्रकार होने का कारण याद करेंगे), लेकिन यह निश्चित रूप से एक वैध और छोटा दृष्टिकोण है। – pickypg

+0

मैं वास्तव में इस उत्तर की सराहना करता हूं। आपके प्रयास के लिए धन्यवाद। –

संबंधित मुद्दे