क्या मुझे जावा में इनिलाइज़र ब्लॉक का उपयोग करना चाहिए?


16

मैं हाल ही में एक जावा निर्माण में आया था जिसे मैंने पहले कभी नहीं देखा था और सोच रहा था कि क्या मुझे इसका उपयोग करना चाहिए। इसे इनिशियल ब्लॉक कहा जाता है

public class Test {
  public Test() { /* first constructor */ }
  public Test(String s) { /* second constructor */ }

  // Non-static initializer block - copied into every constructor:
  {
    doStuff();
  }
}

कोड ब्लॉक को प्रत्येक कंस्ट्रक्टर में कॉपी किया जाएगा, यानी यदि आपके पास कई कंस्ट्रक्टर हैं तो आपको कोड को फिर से लिखना नहीं होगा।

हालाँकि, मुझे इस सिंटैक्स का उपयोग करते हुए तीन मुख्य कमियाँ दिखाई देती हैं:

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

यदि मेरे उपरोक्त कथन सत्य हैं, तो यह भाषा निर्माण क्यों (और कब) शुरू किया गया था? क्या कोई वैध उपयोग के मामले हैं?


3
आपके द्वारा पोस्ट किया गया उदाहरण में प्रारंभिक ब्लॉक की तरह दिखने वाली कोई भी चीज़ शामिल नहीं है।
साइमन बी

6
@SimonBarker फिर से देखो - { doStuff(); }वर्ग स्तर पर एक इनिशलाइज़र ब्लॉक है।
आमोन

@SimonBarker कोड ब्लॉक जो आसपास हैdoStuff()
मोनिका को बहाल करें - 12:03


2
"एस] कोड ब्लॉक के क्रम को बदलने से वास्तव में कोड बदल जाएगा।" और यह कि वैरिएबल इनिशियलाइज़र या कोड की अलग-अलग लाइनों के क्रम को बदलने से कैसे अलग है? यदि कोई निर्भरताएँ नहीं हैं, तो कोई हानि नहीं होती है, और यदि निर्भरताएँ होती हैं, तो निर्भरताओं को क्रम से बाहर रखना कोड की अलग-अलग पंक्तियों के लिए निर्भरता को गलत करने के समान है। सिर्फ इसलिए कि जावा आपको परिभाषित करने से पहले विधियों और कक्षाओं को संदर्भित करता है, इसका मतलब यह नहीं है कि जावा में आदेश-निर्भर कोड दुर्लभ है।
JAB

जवाबों:


20

ऐसे दो मामले हैं जहां मैं इनिशियलाइज़र ब्लॉक का उपयोग करता हूं।

पहला सदस्य अंतिम सदस्यों को शुरू करने के लिए है। जावा में, आप अंतिम सदस्य को घोषणा के साथ इनलाइन कर सकते हैं, या आप इसे कंस्ट्रक्टर में शुरू कर सकते हैं। एक विधि में, अंतिम सदस्य को निर्दिष्ट करने से मना किया जाता है।

यह मान्य है:

final int val = 2;

यह भी मान्य है:

final int val;

MyClass() {
    val = 2;
}

यह अमान्य है:

final int val;

MyClass() {
    init();
}

void init() {
    val = 2;  // cannot assign to 'final' field in a method
}

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

final int val;
final int squareVal;

MyClass(int v, String s) {
    this.val = v;
    this.s = s;
}

MyClass(Point p, long id) {
    this.val = p.x;
    this.id = id;
}

{
    squareVal = val * val;
}

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

private Map<String, String> days = new HashMap<String, String>();
{
    days.put("mon", "monday");
    days.put("tue", "tuesday");
    days.put("wed", "wednesday");
    days.put("thu", "thursday");
    days.put("fri", "friday");
    days.put("sat", "saturday");
    days.put("sun", "sunday");
}

यह विधि कॉल नहीं है जो अमान्य है। यह init मेथड के अंदर का कोड है जो अमान्य है। केवल कंस्ट्रक्टर और इनसिटलाइज़र ब्लॉक एक अंतिम सदस्य चर को असाइन कर सकते हैं, इस प्रकार इनिट में असाइनमेंट संकलित नहीं होगा।
बरजक

आपका चौथा कोड ब्लॉक संकलित नहीं करता है। Initalizer ब्लॉक सभी कंस्ट्रक्टर्स से पहले चलता है , इसलिए अनइंस्टॉल किए गए squareVal = val * valमानों तक पहुंचने के बारे में शिकायत करेगा। प्रारंभिक ब्लॉक संभवतः कंस्ट्रक्टर को दिए गए किसी भी तर्क पर निर्भर नहीं कर सकते हैं। उस तरह की समस्या के लिए मैंने जो सामान्य समाधान देखा है, वह जटिल तर्क के साथ एक एकल "बेस" कंस्ट्रक्टर को परिभाषित करना है, और उस एक के संदर्भ में अन्य सभी कंस्ट्रक्टरों को परिभाषित करना है। उदाहरण के शुरुआती उपयोगकर्ता, वास्तव में, उस पैटर्न के साथ प्रतिस्थापित किए जा सकते हैं।
मालनॉर्मलू

11

सामान्य तौर पर, गैर-स्थैतिक आरम्भिक ब्लॉक का उपयोग न करें (और शायद स्थैतिक से भी बचें)।

भ्रमित करने वाला सिंटेक्स

इस प्रश्न को देखते हुए, 3 उत्तर हैं, फिर भी आपने इस वाक्य रचना के साथ 4 लोगों को बेवकूफ बनाया। मैं उनमें से एक था और मैं 16 वर्षों से जावा लिख ​​रहा हूं! स्पष्ट रूप से, वाक्यविन्यास संभावित रूप से त्रुटि प्रवण है! मैं इससे दूर रहता।

टेलिस्कोपिंग कंस्ट्रक्टर्स

वास्तव में सरल सामान के लिए, आप इस भ्रम से बचने के लिए "दूरबीन" का उपयोग कर सकते हैं:

public class Test {
    private String something;

    // Default constructor does some things
    public Test() { doStuff(); }

    // Other constructors call the default constructor
    public Test(String s) {
        this(); // Call default constructor
        something = s;
    }
}

बिल्डर पैटर्न

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

public class Test {
    // Value can be final (immutable)
    private final String something;

    // Private constructor.
    private Test(String s) { something = s; }

    // Static method to get a builder
    public static Builder builder() { return new Builder(); }

    // builder class accumulates values until a valid Test object can be created. 
    private static class Builder {
        private String tempSomething;
        public Builder something(String s) {
            tempSomething = s;
            return this;
        }
        // This is our factory method for a Test class.
        public Test build() {
            Test t = new Test(tempSomething);
            // Here we do your extra initialization after the
            // Test class has been created.
            doStuff();
            // Return a valid, potentially immutable Test object.
            return t;
        }
    }
}

// Now you can call:
Test t = Test.builder()
             .setString("Utini!")
             .build();

स्टेटिक इनिशिएटिव लूप्स

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

आलसी प्रारंभिक

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

डेटा परिभाषा

डेटा संरचनाओं के निर्माण के लिए स्थैतिक आरंभीकरण के बजाय, (अन्य उत्तरों में उदाहरणों की तुलना में), मैं अब पगुरो की अपरिवर्तनीय डेटा परिभाषा सहायक कार्यों का उपयोग करता हूं :

private ImMap<String,String> days =
        map(tup("mon", "monday"),
            tup("tue", "tuesday"),
            tup("wed", "wednesday"),
            tup("thu", "thursday"),
            tup("fri", "friday"),
            tup("sat", "saturday"),
            tup("sun", "sunday"));

Conculsion

जावा की शुरुआत में, शुरुआती ब्लॉक कुछ काम करने का एकमात्र तरीका था, लेकिन अब वे भ्रमित कर रहे हैं, त्रुटि प्रवण, और ज्यादातर मामलों में बेहतर विकल्प (ऊपर विस्तृत) द्वारा प्रतिस्थापित किया गया है। यदि आप उन्हें लीगेसी कोड में देखते हैं, या वे एक परीक्षण पर आते हैं, तो मुझे शुरुआती ब्लॉक के बारे में जानना दिलचस्प है, लेकिन अगर मैं कोड की समीक्षा कर रहा था और मैंने एक नए कोड में देखा, तो मैं आपसे पूछना चाहता हूं कि कोई भी क्यों नहीं ऊपर दिए गए विकल्प आपके कोड को अंगूठा देने से पहले उपयुक्त थे।


3

एक उदाहरण चर के आरंभीकरण के अलावा जिसे घोषित किया गया है final(देखें बर्कज का उत्तर ), मैं staticआरंभीकरण ब्लॉक का भी उल्लेख करूंगा ।

आप उन्हें "स्थिर अवरोधक" के रूप में उपयोग कर सकते हैं।

इस तरह आप एक स्थिर वैरिएबल पर जटिल इनिशियलाइज़ेशन कर सकते हैं जब पहली बार क्लास को संदर्भित किया जाता है।

यहाँ एक उदाहरण है जो बरजक से प्रेरित है:

public class dayHelper(){
    private static Map<String, String> days = new HashMap<String, String>();
    static {
        days.put("mon", "monday");
        days.put("tue", "tuesday");
        days.put("wed", "wednesday");
        days.put("thu", "thursday");
        days.put("fri", "friday");
        days.put("sat", "saturday");
        days.put("sun", "sunday");
    }
    public static String getLongName(String shortName){
         return days.get(shortName);
    }
}

1

जैसा कि गैर-स्टैटिक इनिशियलाइज़र ब्लॉक के रूप में fas का संबंध है, उनका नंगे कार्य अनाम कक्षाओं में डिफ़ॉल्ट कंस्ट्रक्टर के रूप में कार्य करना है। यह मूल रूप से उनके अस्तित्व का एकमात्र अधिकार है।


0

मैं पूरी तरह से 1, 2, 3 के बयानों से सहमत हूं। मैं भी इन कारणों से ब्लॉक इनिशियलाइज़र का उपयोग नहीं करता हूं और मुझे नहीं पता कि यह जावा में क्यों मौजूद है।

हालांकि, मैं एक मामले में स्टेटिक ब्लॉक इनिशियलाइज़र का उपयोग करने के लिए मजबूर हूं : जब मुझे एक स्थिर फ़ील्ड को इंस्टेंट करना होगा जिसका कंस्ट्रक्टर एक चेक किए गए अपवाद को फेंक सकता है।

private static final JAXBContext context = JAXBContext.newInstance(Foo.class); //doesn't compile

लेकिन इसके बजाय आपको करना होगा:

private static JAXBContext context;
static {
    try
    {
        context = JAXBContext.newInstance(Foo.class);
    }
    catch (JAXBException e)
    {
        //seriously...
    }
}

मैं इस मुहावरे बहुत बदसूरत (यह भी निशान के रोकता है लगता है contextके रूप में final), लेकिन यह एक ही तरीका है जावा द्वारा समर्थित इस तरह के क्षेत्रों को प्रारंभ करने के लिए है।


मुझे लगता है कि यदि आप context = null;अपने पकड़ ब्लॉक में सेट करते हैं, तो आप संदर्भ को अंतिम रूप देने में सक्षम हो सकते हैं।
ग्लेनपेटर्सन

@GlenPeterson मैंने कोशिश की, लेकिन यह संकलित नहीं करता है:The final field context may already have been assigned
स्पॉट

उफ़! मुझे यकीन है कि आप अपने संदर्भ को अंतिम बना सकते हैं यदि आप स्थैतिक ब्लॉक के अंदर एक स्थानीय चर का परिचय देते हैं:static { JAXBContext tempCtx = null; try { tempCtx = JAXBContext.newInstance(Foo.class); } catch (JAXBException ignored) { ; } context = tempCtx; }
GlenPeterson
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.