Enum के निर्माता स्थैतिक क्षेत्रों तक क्यों नहीं पहुंच सकते हैं?


110

Enum के निर्माता स्थैतिक क्षेत्रों और विधियों तक क्यों नहीं पहुंच सकते हैं? यह पूरी तरह से एक वर्ग के साथ मान्य है, लेकिन एक एनम के साथ अनुमति नहीं है।

मैं जो करने की कोशिश कर रहा हूं वह मेरे स्थिर उदाहरणों को स्थिर मानचित्र में संग्रहीत करता है। इस उदाहरण कोड पर विचार करें जो अमूर्तता से देखने की अनुमति देता है:

public enum Day {
    Sunday("Sun"), Monday("Mon"), Tuesday("Tue"), Wednesday("Wed"), Thursday("Thu"), Friday("Fri"), Saturday("Sat");

    private final String abbreviation;

    private static final Map<String, Day> ABBREV_MAP = new HashMap<String, Day>();

    private Day(String abbreviation) {
        this.abbreviation = abbreviation;
        ABBREV_MAP.put(abbreviation, this);  // Not valid
    }

    public String getAbbreviation() {
        return abbreviation;
    }

    public static Day getByAbbreviation(String abbreviation) {
        return ABBREV_MAP.get(abbreviation);
    }
}

यह काम नहीं करेगा क्योंकि एनम अपने निर्माता में स्थिर संदर्भों की अनुमति नहीं देता है। हालाँकि यह सिर्फ एक वर्ग के रूप में लागू होने पर काम करता है:

public static final Day SUNDAY = new Day("Sunday", "Sun");
private Day(String name, String abbreviation) {
    this.name = name;
    this.abbreviation = abbreviation;
    ABBREV_MAP.put(abbreviation, this);  // Valid
}

जवाबों:


113

कंस्ट्रक्टर को स्थिर क्षेत्रों से पहले बुलाया जाता है, सभी को इनिशियलाइज़ किया गया है, क्योंकि स्टैटिक फ़ील्ड्स (एनम वैल्यूज़ को दर्शाने वाले सहित) को टेक्सटाइल ऑर्डर में इनिशियलाइज़ किया जाता है, और एनम वैल्यूज़ हमेशा अन्य फ़ील्ड्स से पहले आते हैं। ध्यान दें कि आपके वर्ग उदाहरण में आपने नहीं दिखाया है जहां ABBREV_MAP को इनिशियलाइज़ किया गया है - यदि यह SUNDAY के बाद है , तो आपको क्लास इनिशियलाइज़ होने पर एक अपवाद मिलेगा।

हां, यह थोड़ा सा दर्द है और शायद बेहतर तरीके से डिजाइन किया गया हो।

हालाँकि, मेरे अनुभव का सामान्य उत्तर यह है कि static {}सभी स्टैटिक इनिशियलाइज़र्स के अंत में एक ब्लॉक होना चाहिए , और EnumSet.allOf सभी मूल्यों को प्राप्त करने के लिए सभी स्टैटिक इनिशियलाइज़ेशन का उपयोग करना चाहिए।


40
यदि आप एक नेस्टेड क्लास को जोड़ते हैं, तो उस के स्टैटिक्स को एक उपयुक्त समय पर आरंभीकृत किया जाएगा।
टॉम हॉन्टिन -

ऊह, अच्छा है। मैंने ऐसा नहीं सोचा था।
जॉन स्कीट

3
एक अजीब सा एक लेकिन अगर आप एक एनम कंस्ट्रक्टर में एक स्थिर विधि कहते हैं जो एक स्थिर मूल्य देता है तो यह ठीक संकलन करेगा - लेकिन यह जो रिटर्न देता है वह उस प्रकार के लिए डिफ़ॉल्ट होगा (यानी 0, 0.0, '\ u0000' या अशक्त), भले ही आप इसे स्पष्ट रूप से सेट करें (जब तक कि यह घोषित न हो final)। लगता है कि पकड़ने के लिए एक मुश्किल हो जाएगा!
मार्क रोड्स

2
क्विक स्पिन-ऑफ प्रश्न @JonSkeet: ऐसा कोई कारण जो आप के EnumSet.allOfबजाय उपयोग करते हैं Enum.values()? मैं पूछता हूं क्योंकि valuesएक प्रेत विधि की तरह है ( Enum.classमैं स्रोत नहीं देख सकता ) और मुझे नहीं पता कि इसका निर्माण कब हुआ
चिरलो

1
@ चिरलो उस बारे में एक सवाल है। ऐसा लगता है कि Enum.values()यदि आप लूप के लिए एक बढ़ाया (क्योंकि यह एक सरणी देता है) के साथ उन पर पुनरावृति की योजना बनाते हैं, तो यह तेज है, लेकिन ज्यादातर इसकी शैली और उपयोग के मामले के बारे में है। यह शायद बेहतर होगा EnumSet.allOf()कि यदि आप कोड लिखना चाहते हैं जो जावा के प्रलेखन में केवल ऐनक के बजाय मौजूद है, लेकिन बहुत से लोग Enum.values()वैसे भी परिचित हैं ।
4castle

31

JLS से कोट , "एनुम बॉडी डिक्लेरेशन" अनुभाग :

इस नियम के बिना, स्पष्ट रूप से उचित कोड एनुम प्रकारों में निहित प्रारंभिकता परिपत्रता के कारण रन समय पर विफल हो जाएगा। ("स्व-टाइप" स्थिर क्षेत्र के साथ किसी भी वर्ग में एक परिपत्रता मौजूद है। यहाँ कोड के प्रकार का एक उदाहरण विफल होगा:

enum Color {
    RED, GREEN, BLUE;
    static final Map<String,Color> colorMap = new HashMap<String,Color>();

    Color() {
       colorMap.put(toString(), this);
    }
}

इस एनुम प्रकार के स्थैतिक आरंभीकरण के कारण NullPointerException को फेंक दिया जाएगा क्योंकि स्थिर चर colorMap अनइंस्टालिज़ किया गया है जब एनम स्थिरांक के लिए निर्माता चलते हैं। उपरोक्त प्रतिबंध यह सुनिश्चित करता है कि ऐसा कोड संकलित नहीं होगा।

ध्यान दें कि उदाहरण आसानी से ठीक से काम करने के लिए refactored जा सकता है:

enum Color {
    RED, GREEN, BLUE;
    static final Map<String,Color> colorMap = new HashMap<String,Color>();

    static {
        for (Color c : Color.values())
            colorMap.put(c.toString(), c);
    }
}

स्टैटिक इनिशियलाइज़ेशन ऊपर से नीचे होता है, क्योंकि रिफलेक्टेड संस्करण स्पष्ट रूप से सही है।


9

शायद यही आप चाहते हैं

public enum Day {
    Sunday("Sun"), 
    Monday("Mon"), 
    Tuesday("Tue"), 
    Wednesday("Wed"), 
    Thursday("Thu"), 
    Friday("Fri"), 
    Saturday("Sat");

    private static final Map<String, Day> ELEMENTS;

    static {
        Map<String, Day> elements = new HashMap<String, Day>();
        for (Day value : values()) {
            elements.put(value.element(), value);
        }
        ELEMENTS = Collections.unmodifiableMap(elements);
    }

    private final String abbr;

    Day(String abbr) {
        this.abbr = abbr;
    }

    public String element() {
        return this.abbr;
    }

    public static Day elementOf(String abbr) {
        return ELEMENTS.get(abbr);
    }
}

उपयोग करना Collections.unmodifiableMap()यहाँ एक बहुत अच्छा अभ्यास है। +1
कोसल

ठीक वही जो मेरे द्वारा खोजा जा रहा था। मुझे Collections.unmodifiableMap देखना भी पसंद है। धन्यवाद!
लीथलिमा

6

समस्या एक नेस्टेड क्लास के माध्यम से हल हुई। पेशेवरों: यह सीपीयू की खपत से कम और बेहतर है। विपक्ष: जेवीएम मेमोरी में एक और वर्ग।

enum Day {

    private static final class Helper {
        static Map<String,Day> ABBR_TO_ENUM = new HashMap<>();
    }

    Day(String abbr) {
        this.abbr = abbr;
        Helper.ABBR_TO_ENUM.put(abbr, this);

    }

    public static Day getByAbbreviation(String abbr) {
        return Helper.ABBR_TO_ENUM.get(abbr);
    }

1

जब एक वर्ग को JVM में लोड किया जाता है तो स्थिर फ़ील्ड को उस क्रम में आरम्भ किया जाता है जिसमें वे कोड में दिखाई देते हैं। उदाहरण के लिए

public class Test4 {
        private static final Test4 test4 = new Test4();
        private static int j = 6;
        Test4() {
            System.out.println(j);
        }
        private static void test() {
        }
        public static void main(String[] args) {
            Test4.test();
        }
    }

आउटपुट 0. होगा। ध्यान दें कि टेस्ट 4 इनिशियलाइज़ेशन स्टैटिक इनिशियलाइज़ेशन प्रोसेस में होता है और इस दौरान j अभी तक इनिशियलाइज़ नहीं हुआ है क्योंकि यह बाद में दिखाई देता है। अब अगर हम स्थैतिक इनिशियलाइज़र्स के ऑर्डर को स्विच करते हैं, तो यह टेस्ट 4 से पहले आता है। आउटपुट 6. होगा। Enums के मामले में हम स्थैतिक क्षेत्रों के क्रम को बदल नहीं सकते हैं। एनम में पहली चीज़ स्थिरांक होनी चाहिए जो वास्तव में एनम प्रकार के स्थिर अंतिम उदाहरण हैं। इन एनमों के लिए यह हमेशा गारंटी देता है कि स्थिर फ़ील्डों को एनम स्थिरांक से पहले आरंभीकृत नहीं किया जाना चाहिए। क्योंकि हम एनम कंस्ट्रक्टर में उपयोग के लिए स्थैतिक फ़ील्ड को कोई समझदार मान नहीं दे सकते हैं , एनम कंस्ट्रक्टर में उन्हें एक्सेस करना अर्थहीन होगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.