जावा में डबल ब्रेस इनिशियलाइज़ेशन क्या है?


305

{{ ... }}जावा में डबल ब्रेस इनिशियलाइज़ेशन सिंटैक्स ( ) क्या है ?




10
डबल ब्रेस इनिशियलाइज़ेशन एक बहुत ही खतरनाक विशेषता है और इसका उपयोग विवेकपूर्ण तरीके से किया जाना चाहिए। यह अनुबंध को बराबर कर सकता है और मुश्किल स्मृति लीक को पेश कर सकता है। यह लेख विवरण का वर्णन करता है।
एंड्री पोलुनिन

जवाबों:


301

डबल ब्रेस आरंभीकरण निर्दिष्ट वर्ग ( बाहरी ब्रेसिज़) से उत्पन्न एक अनाम वर्ग बनाता है , और उस वर्ग ( आंतरिक ब्रेसिज़) के भीतर एक इनिशियर ब्लॉक प्रदान करता है । जैसे

new ArrayList<Integer>() {{
   add(1);
   add(2);
}};

ध्यान दें कि इस डबल ब्रेस इनिशियलाइज़ेशन का उपयोग करने का एक प्रभाव यह है कि आप अनाम आंतरिक कक्षाएं बना रहे हैं। निर्मित वर्ग में thisआसपास के बाहरी वर्ग के लिए एक अंतर्निहित सूचक होता है। जबकि सामान्य रूप से कोई समस्या नहीं है, यह कुछ परिस्थितियों में दु: ख पैदा कर सकता है जैसे कि धारावाहिक या कचरा एकत्र करना, और यह इस बारे में जागरूक होने के लायक है।


11
आंतरिक और बाहरी ब्रेसिज़ के अर्थ को स्पष्ट करने के लिए धन्यवाद। मुझे आश्चर्य है कि अचानक एक विशेष अर्थ के साथ दो ब्रेसिज़ की अनुमति क्यों होती है, जब वे वास्तव में सामान्य जावा निर्माण होते हैं जो केवल कुछ जादुई नई चाल के रूप में दिखाई देते हैं। हालांकि इस तरह की चीजें मुझे जावा सिंटैक्स से प्रश्न बनाती हैं। यदि आप पहले से ही एक विशेषज्ञ नहीं हैं, तो यह पढ़ने और लिखने के लिए बहुत मुश्किल हो सकता है।
जैकथेस्टर

4
"मैजिक सिंटैक्स" इस तरह से कई भाषाओं में मौजूद है, उदाहरण के लिए लगभग सभी सी-लाइक भाषाएँ "x -> 0" के "लूप्स" के लिए जाती हैं जो लूप के लिए "x--> 0" अजीब है। अंतरिक्ष प्लेसमेंट।
जोकिम सॉयर

17
हम केवल यह निष्कर्ष निकाल सकते हैं कि "डबल ब्रेस इनिशियलाइज़ेशन" अपने आप ही मौजूद नहीं है, यह केवल एक अनाम वर्ग और एक इनिशियलाइज़र ब्लॉक बनाने का एक संयोजन है , जो एक बार संयुक्त हो जाने पर, एक सिंटैक्टिकल निर्माण की तरह दिखता है , लेकिन वास्तव में, यह 'नहीं है टी।
एमसी सम्राट

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

294

हर बार जब कोई डबल ब्रेस इनिशियलाइज़ेशन का उपयोग करता है, तो एक बिल्ली का बच्चा मारा जाता है।

वाक्यविन्यास असामान्य होने के अलावा और वास्तव में मुहावरेदार नहीं है (स्वाद बहस योग्य है, निश्चित रूप से), आप अनावश्यक रूप से अपने आवेदन में दो महत्वपूर्ण समस्याएं पैदा कर रहे हैं, जिनके बारे में मैंने हाल ही में यहां और अधिक विस्तार से ब्लॉग किया है

1. आप बहुत अधिक अनाम कक्षाएं बना रहे हैं

हर बार जब आप डबल ब्रेस इनिशियलाइज़ेशन का उपयोग करते हैं तो एक नया वर्ग बनाया जाता है। उदाहरण के लिए:

Map source = new HashMap(){{
    put("firstName", "John");
    put("lastName", "Smith");
    put("organizations", new HashMap(){{
        put("0", new HashMap(){{
            put("id", "1234");
        }});
        put("abc", new HashMap(){{
            put("id", "5678");
        }});
    }});
}};

... इन कक्षाओं का उत्पादन करेंगे:

Test$1$1$1.class
Test$1$1$2.class
Test$1$1.class
Test$1.class
Test.class

यह आपके क्लास लोडर के लिए बहुत अधिक उपरि है - बिना कुछ लिए! यदि आप इसे एक बार करते हैं, तो निश्चित रूप से यह अधिक आरंभिक समय नहीं लेगा। लेकिन अगर आप अपने उद्यम आवेदन में यह 20'000 गुना करते हैं ... तो बस "सिंटेक्स शुगर" के लिए यह सब स्मृति को ढेर कर देता है?

2. आप संभावित रूप से स्मृति रिसाव बना रहे हैं!

यदि आप उपरोक्त कोड लेते हैं और उस नक्शे को किसी विधि से वापस करते हैं, तो उस विधि के कॉलर बहुत ही भारी संसाधनों पर निर्बाध रूप से पकड़े जा सकते हैं जो कचरा एकत्र नहीं किया जा सकता है। निम्नलिखित उदाहरण पर विचार करें:

public class ReallyHeavyObject {

    // Just to illustrate...
    private int[] tonsOfValues;
    private Resource[] tonsOfResources;

    // This method almost does nothing
    public Map quickHarmlessMethod() {
        Map source = new HashMap(){{
            put("firstName", "John");
            put("lastName", "Smith");
            put("organizations", new HashMap(){{
                put("0", new HashMap(){{
                    put("id", "1234");
                }});
                put("abc", new HashMap(){{
                    put("id", "5678");
                }});
            }});
        }};

        return source;
    }
}

Mapअब दिए गए एन्क्लोज़िंग इंस्टेंस के संदर्भ में रिटर्न होगा ReallyHeavyObject। आप शायद यह जोखिम नहीं लेना चाहते:

मेमोरी लीक यहीं

Http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-ant-pattern/ से छवि

3. आप यह दिखावा कर सकते हैं कि जावा में मानचित्र शाब्दिक हैं

आपके वास्तविक प्रश्न का उत्तर देने के लिए, लोग इस वाक्यविन्यास का उपयोग यह ढोंग करने के लिए कर रहे हैं कि जावा के पास कुछ शब्द हैं जैसे कि मौजूदा मोबाइल साहित्य के समान, नक्शा मानचित्र।

String[] array = { "John", "Doe" };
Map map = new HashMap() {{ put("John", "Doe"); }};

कुछ लोगों को यह वाक्यात्मक रूप से उत्तेजक लग सकता है।


11
"आप बहुत अधिक अनाम कक्षाएं बना रहे हैं" - कैसे (कहते हैं) स्काला अनाम कक्षाएं बनाता है, मुझे बहुत यकीन नहीं है कि यह एक बड़ी समस्या है
ब्रायन एग्न्यू

2
क्या स्थैतिक मानचित्र घोषित करने का यह एक वैध और अच्छा तरीका नहीं है? यदि एक HashMap के साथ आरंभ किया जाता है {{...}}और एक staticक्षेत्र के रूप में घोषित किया जाता है , तो किसी भी संभावित मेमोरी लीक, केवल एक अनाम वर्ग और कोई संलग्न उदाहरण संदर्भ नहीं होना चाहिए, है ना?
लोरेंजो-एस

8
@ लोरेंजो-एस: हां, 2) और 3) तब लागू नहीं होते हैं, केवल 1)। सौभाग्य से, जावा 9 के साथ, अंत Map.of()में उस उद्देश्य के लिए है, इसलिए यह एक बेहतर समाधान होगा
लुकास ईडर

3
यह ध्यान देने योग्य हो सकता है कि आंतरिक मानचित्रों में बाहरी मानचित्रों के संदर्भ भी होते हैं और इसलिए, अप्रत्यक्ष रूप से ReallyHeavyObject। इसके अलावा, अनाम आंतरिक वर्ग, वर्ग निकाय के भीतर उपयोग किए जाने वाले सभी स्थानीय चरों पर कब्जा कर लेते हैं, इसलिए यदि आप इस पैटर्न के साथ संग्रह या मानचित्र को इनिशियलाइज़ करने के लिए न केवल स्थिरांक का उपयोग करते हैं, तो आंतरिक वर्ग के उदाहरण उन सभी को पकड़ लेंगे और वास्तव में हटाए जाने पर भी उनका संदर्भ लेते रहेंगे। संग्रह या मानचित्र। तो यह मामला है कि, इन उदाहरणों को संदर्भों के लिए आवश्यक स्मृति के रूप में केवल दो बार की आवश्यकता नहीं है, लेकिन उस संबंध में एक और स्मृति रिसाव है।
होल्गर

41
  • पहला ब्रेस एक नया बेनामी इनर क्लास बनाता है।
  • ब्रेस का दूसरा सेट क्लास में स्टेटिक ब्लॉक जैसे इंस्टेंस इनिशियलाइज़र बनाता है।

उदाहरण के लिए:

   public class TestHashMap {
    public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<String,String>(){
        {
            put("1", "ONE");
        }{
            put("2", "TWO");
        }{
            put("3", "THREE");
        }
        };
        Set<String> keySet = map.keySet();
        for (String string : keySet) {
            System.out.println(string+" ->"+map.get(string));
        }
    }

}

यह काम किस प्रकार करता है

पहले ब्रेस एक नया बेनामी इनर क्लास बनाता है। ये आंतरिक कक्षाएं अपने मूल वर्ग के व्यवहार तक पहुंचने में सक्षम हैं। तो, हमारे मामले में, हम वास्तव में हैशसेट वर्ग का एक उपवर्ग बना रहे हैं, इसलिए यह आंतरिक वर्ग पुट () पद्धति का उपयोग करने में सक्षम है।

और ब्रेसिज़ का दूसरा सेट उदाहरण शुरुआती के अलावा कुछ भी नहीं है। यदि आप कोर जावा अवधारणाओं को याद दिलाते हैं, तो आप संरचना जैसे समान ब्रेस के कारण स्थैतिक इनिशियलाइज़र के साथ आसानी से इंस्टाइज़र ब्लॉक को जोड़ सकते हैं। केवल अंतर यह है कि स्थिर इनिशियलाइज़र को स्थैतिक कीवर्ड के साथ जोड़ा जाता है, और केवल एक बार चलाया जाता है; कोई फर्क नहीं पड़ता कि आप कितनी वस्तुओं का निर्माण करते हैं।

अधिक


24

डबल ब्रेस इनिशियलाइज़ेशन के मज़ेदार एप्लिकेशन के लिए, जावा में डवेमी के एरे को देखें ।

अंश

private static class IndustrialRaverMonkey
  extends Creature.Base {{
    life = 46;
    strength = 35;
    charisma = 91;
    weapon = 2;
  }}

private static class DwarvenAngel
  extends Creature.Base {{
    life = 540;
    strength = 6;
    charisma = 144;
    weapon = 50;
  }}

और अब, BattleOfGrottoOfSausageSmellsऔर ... चंकी बेकन के लिए तैयार रहें !


16

मुझे लगता है कि यह तनावपूर्ण है कि जावा में "डबल ब्रेस इनिशियलाइज़ेशन" जैसी कोई चीज़ नहीं है । Oracle वेब-साइट में यह शब्द नहीं है। इस उदाहरण में दो विशेषताएं एक साथ उपयोग की जाती हैं: अनाम वर्ग और इनिशलाइज़र ब्लॉक। पुराने इनिशियलाइज़र ब्लॉक की तरह लगता है कि डेवलपर्स द्वारा भूल गए हैं और इस विषय में कुछ भ्रम पैदा करते हैं। Oracle डॉक्स से उद्धरण :

उदाहरण के लिए शुरुआती ब्लॉक वैरिएबल सिर्फ स्टैटिक इनिशियलाइज़र ब्लॉक की तरह दिखते हैं, लेकिन स्टैटिक कीवर्ड के बिना:

{
    // whatever code is needed for initialization goes here
}

11

1- डबल ब्रेसेस जैसी कोई चीज नहीं है:
मैं यह बताना चाहता हूं कि डबल ब्रेस इनिशियलाइजेशन जैसी कोई चीज नहीं है। केवल सामान्य पारंपरिक एक ब्रेस इनिशियलाइज़ेशन ब्लॉक है। दूसरे ब्रेसिज़ ब्लॉक का आरंभीकरण से कोई लेना-देना नहीं है। उत्तर कहते हैं कि वे दो ब्रेसिज़ किसी चीज़ को शुरू करते हैं, लेकिन यह ऐसा नहीं है।

2- यह केवल अनाम कक्षाओं के बारे में नहीं है, बल्कि सभी वर्गों के बारे में है:
लगभग सभी उत्तर यह बताते हैं कि अनाम आंतरिक कक्षाओं को बनाते समय इसका उपयोग किया जाता है। मुझे लगता है कि उन उत्तरों को पढ़ने वाले लोगों को यह आभास होगा कि इसका उपयोग केवल अनाम आंतरिक कक्षाओं को बनाते समय किया जाता है। लेकिन इसका उपयोग सभी वर्गों में किया जाता है। उन जवाबों को पढ़कर ऐसा लगता है जैसे कुछ ब्रांड की नई खासियत है जो गुमनाम कक्षाओं के लिए समर्पित है और मुझे लगता है कि यह भ्रामक है।

3- उद्देश्य सिर्फ एक दूसरे के बाद कोष्ठक रखने के बारे में है, नई अवधारणा नहीं:
आगे जाकर, यह प्रश्न उस स्थिति के बारे में बात करता है जब दूसरा उद्घाटन ब्रैकेट पहले खोलने वाले ब्रैकेट के बाद होता है। जब सामान्य वर्ग में उपयोग किया जाता है तो आमतौर पर दो ब्रेसिज़ के बीच कुछ कोड होता है, लेकिन यह पूरी तरह से एक ही बात है। तो यह कोष्ठक रखने की बात है। इसलिए मुझे लगता है कि हमें यह नहीं कहना चाहिए कि यह कुछ नई रोमांचक चीज है, क्योंकि यह वह चीज है जिसे हम सभी जानते हैं, लेकिन सिर्फ कोष्ठक के बीच कुछ कोड के साथ लिखा जाता है। हमें "डबल ब्रेस इनिशियलाइज़ेशन" नामक नई अवधारणा नहीं बनानी चाहिए।

4- नेस्टेड अनाम कक्षाओं को बनाने में दो ब्रेसिज़ के साथ कुछ नहीं करना है:
मैं एक तर्क से सहमत नहीं हूं कि आप बहुत अधिक अनाम कक्षाएं बनाते हैं। आप उन्हें एक इनिशियलाइज़ेशन ब्लॉक के कारण नहीं बना रहे हैं, बल्कि सिर्फ इसलिए कि आप उन्हें बनाते हैं। वे तब भी बनाए जाएंगे जब आपने दो ब्रेसिज़ इनिशियलाइज़ेशन का उपयोग नहीं किया था, इसलिए वे समस्याएं बिना इनिशियलाइज़ेशन के भी हो जाएंगी ... इनिशियलाइज़ेशन वह कारक नहीं है जो इनिशियलाइज़्ड ऑब्जेक्ट बनाता है।

इसके अतिरिक्त हमें इस गैर-मौजूद चीज़ "डबल ब्रेस इनिशियलाइज़ेशन" या सामान्य एक ब्रैकेट इनिशियलाइज़ेशन का उपयोग करके बनाई गई समस्या के बारे में बात नहीं करनी चाहिए, क्योंकि वर्णित समस्याएं केवल अनाम वर्ग बनाने के कारण मौजूद हैं, इसलिए इसका मूल प्रश्न से कोई लेना-देना नहीं है। लेकिन सभी उत्तर पाठकों को यह आभास कराते हैं कि यह गुमनाम वर्गों के निर्माण का दोष नहीं है, बल्कि इस बुराई (अस्तित्वहीन) चीज को "डबल ब्रेस इनिशियलाइज़ेशन" कहा जाता है।


9

डबल ब्रेस इनिशियलाइज़ेशन के सभी नकारात्मक प्रभावों से बचने के लिए, जैसे:

  1. टूटी हुई "बराबर" संगतता।
  2. कोई चेक नहीं किया गया, जब सीधे असाइनमेंट का उपयोग करें।
  3. संभव स्मृति लीक।

अगले काम करो:

  1. विशेष रूप से डबल ब्रेस आरंभीकरण के लिए अलग "बिल्डर" वर्ग बनाएं।
  2. डिफ़ॉल्ट मान के साथ फ़ील्ड घोषित करें।
  3. उस कक्षा में ऑब्जेक्ट निर्माण विधि रखो।

उदाहरण:

public class MyClass {
    public static class Builder {
        public int    first  = -1        ;
        public double second = Double.NaN;
        public String third  = null      ;

        public MyClass create() {
            return new MyClass(first, second, third);
        }
    }

    protected final int    first ;
    protected final double second;
    protected final String third ;

    protected MyClass(
        int    first ,
        double second,
        String third
    ) {
        this.first = first ;
        this.second= second;
        this.third = third ;
    }

    public int    first () { return first ; }
    public double second() { return second; }
    public String third () { return third ; }
}

उपयोग:

MyClass my = new MyClass.Builder(){{ first = 1; third = "3"; }}.create();

लाभ:

  1. बस उपयोग करने के लिए।
  2. "संगतता" के बराबर न टूटें।
  3. आप निर्माण विधि में जांच कर सकते हैं।
  4. कोई स्मृति लीक नहीं।

नुकसान:

  • कोई नहीं।

और, परिणामस्वरूप, हमारे पास सबसे सरल जावा बिल्डर पैटर्न है।

जीथब पर सभी नमूने देखें: जावा-एसएफ-बिल्डर-सरल-उदाहरण


4

यह - अन्य उपयोगों के बीच - संग्रह को आरंभ करने के लिए एक शॉर्टकट है। और अधिक जानें ...


2
खैर, यह इसके लिए एक आवेदन है, लेकिन किसी भी तरह से केवल एक ही नहीं है।
स्कैफ़मैन

4

क्या आपका मतलब इस तरह की किसी चीज से है?

List<String> blah = new ArrayList<String>(){{add("asdfa");add("bbb");}};

यह सृजन समय (हैक) में एक सरणी सूची आरंभीकरण है


4

संग्रह को आरंभ करने के लिए आप कुछ जावा स्टेटमेंट को लूप के रूप में रख सकते हैं:

List<Character> characters = new ArrayList<Character>() {
    {
        for (char c = 'A'; c <= 'E'; c++) add(c);
    }
};

Random rnd = new Random();

List<Integer> integers = new ArrayList<Integer>() {
    {
         while (size() < 10) add(rnd.nextInt(1_000_000));
    }
};

लेकिन यह मामला प्रदर्शन को प्रभावित करता है, इस चर्चा की जाँच करें


4

जैसा कि @Lukas Eder द्वारा बताया गया है कि संग्रह के दोहरे ब्रेसिज़ की शुरुआत से बचा जाना चाहिए।

यह एक अनाम आंतरिक वर्ग बनाता है, और चूंकि सभी आंतरिक वर्ग माता-पिता के उदाहरण के संदर्भ में इसे रख सकते हैं - और 99% संभावना है - कचरा संग्रहण को रोकें यदि इन संग्रह वस्तुओं को केवल घोषित करने की तुलना में अधिक वस्तुओं द्वारा संदर्भित किया जाता है।

जावा 9 सुविधा तरीकों शुरू की है List.of, Set.ofऔर Map.ofहै, जो बजाय प्रयोग किया जाना चाहिए। वे डबल-ब्रेस इनिशियलाइज़र की तुलना में तेज़ और अधिक कुशल हैं।


0

पहला ब्रेस एक नया बेनामी क्लास बनाता है और ब्रेस का दूसरा सेट स्टैटिक ब्लॉक की तरह एक इंस्टेंस इनिशियलाइज़र बनाता है।

जैसे दूसरों ने इशारा किया है, इसका उपयोग करना सुरक्षित नहीं है।

हालाँकि, आप इस विकल्प का उपयोग संग्रह को आरंभ करने के लिए हमेशा कर सकते हैं।

  • जावा 8
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
  • जावा ९
List<String> list = List.of("A", "B", "C");

-1

यह फ़्लैश और vbscript में लोकप्रिय कीवर्ड के समान ही प्रतीत होगा। यह जो thisकुछ भी नहीं है उसे बदलने की एक विधि है ।


ज़रुरी नहीं। यह कहना एक नया वर्ग बनाने की तरह है जो बदलने की एक विधि thisहै। वाक्यविन्यास सिर्फ एक अनाम वर्ग बनाता है (इसलिए किसी भी संदर्भ को thisउस नए अनाम वर्ग की वस्तु के रूप में संदर्भित किया जाएगा), और फिर {...}नए बनाए गए इंस्टेंस को आरंभीकृत करने के लिए एक शुरुआती ब्लॉक का उपयोग करता है ।
20
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.