स्थैतिक पर गैर-स्थिर आंतरिक वर्ग क्यों पसंद करते हैं?


37

यह सवाल है कि जावा में नेस्टेड क्लास बनाने के लिए स्टैटिक नेस्टेड क्लास है या इनर नेस्टेड क्लास है। मैंने इधर-उधर और स्टैक ओवरफ्लो पर खोज की, लेकिन वास्तव में इस निर्णय के डिजाइन निहितार्थ के बारे में कोई सवाल नहीं कर सका।

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

यहाँ स्थिर नेस्टेड वर्गों का उपयोग करने के प्रभाव के बारे में मेरी समझ है:

  • कम युग्मन: हम आम तौर पर कम युग्मन प्राप्त करते हैं, क्योंकि वर्ग सीधे अपने बाहरी वर्ग की विशेषताओं तक नहीं पहुँच सकता है। कम युग्मन का मतलब आमतौर पर बेहतर कोड गुणवत्ता, आसान परीक्षण, रीफैक्टरिंग आदि है।
  • एकल Class: क्लास लोडर को हर बार एक नए वर्ग का ध्यान रखने की आवश्यकता नहीं है, हम बाहरी वर्ग का एक ऑब्जेक्ट बनाते हैं। हमें बस एक ही वर्ग के लिए नई वस्तुएँ मिलती हैं।

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

तो मेरा प्रश्न इस पर आ जाता है: क्या मैं यह मानने में गलत हूं कि गैर-स्थिर आंतरिक वर्गों के लिए उपलब्ध विशेषता पहुंच उच्च युग्मन की ओर ले जाती है, इसलिए कोड की गुणवत्ता कम होती है, और मुझे लगता है कि इससे (गैर-अनाम) नेस्टेड कक्षाओं में प्रवेश करना चाहिए आम तौर पर स्थिर हो?

या दूसरे शब्दों में: क्या कोई ठोस कारण है कि कोई नेस्टेड इनर क्लास पसंद करेगा?


2
से जावा ट्यूटोरियल : "। उपयोग एक गैर स्थिर नेस्टेड वर्ग (या भीतरी वर्ग) यदि आप एक संलग्न उदाहरण के गैर सरकारी क्षेत्रों और तरीकों के उपयोग की आवश्यकता होती है, तो आप इस पहुँच की आवश्यकता नहीं है एक स्थिर नेस्टेड वर्ग का प्रयोग करें।"
gnat

5
ट्यूटोरियल उद्धरण के लिए धन्यवाद, लेकिन यह सिर्फ फिर से पुनरावृत्त करता है कि अंतर क्या है, इसलिए नहीं कि आप उदाहरण क्षेत्रों तक कैसे पहुंचना चाहते हैं।
फ्रैंक

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

1
यदि आंतरिक वर्ग को संलग्न वर्ग के साथ युग्मित नहीं किया गया है, तो संभवत: यह पहले स्थान पर आंतरिक वर्ग नहीं होना चाहिए।
केविन क्रुमविडे

जवाबों:


23

जोशुआ बलोच अपनी पुस्तक "प्रभावी जावा द्वितीय संस्करण" के आइटम 22 में बताता है कि किस तरह के नेस्टेड क्लास का उपयोग करना है और क्यों। नीचे कुछ उद्धरण दिए गए हैं:

एक स्थिर सदस्य वर्ग का एक सामान्य उपयोग एक सार्वजनिक सहायक वर्ग के रूप में है, जो केवल अपने बाहरी वर्ग के साथ संयोजन के रूप में उपयोगी है। उदाहरण के लिए, एक कैलकुलेटर द्वारा समर्थित संचालन का वर्णन करने वाले एक एनम पर विचार करें। ऑपरेशन एनम को कक्षा का एक सार्वजनिक स्थैतिक सदस्य होना चाहिए Calculator। ग्राहकों के Calculatorनाम Calculator.Operation.PLUSऔर फिर जैसे नामों का उपयोग करके संचालन का उल्लेख कर सकते हैं Calculator.Operation.MINUS

नॉनस्टैटिक सदस्य वर्ग का एक सामान्य उपयोग एक एडेप्टर को परिभाषित करना है जो बाहरी वर्ग के उदाहरण को कुछ असंबंधित वर्ग के उदाहरण के रूप में देखा जा सकता है। उदाहरण के लिए, के कार्यान्वयन Mapइंटरफ़ेस आम तौर पर उनके लागू करने के लिए nonstatic सदस्य वर्गों का उपयोग संग्रह देखा गया , जिसके द्वारा लौटाए गए Mapकी keySet, entrySetऔर valuesतरीकों। इसी तरह, संग्रह इंटरफेस के कार्यान्वयन, जैसे कि Setऔर List, आम तौर पर अपने पुनरावृत्तियों को लागू करने के लिए गैर-सदस्य सदस्य वर्गों का उपयोग करते हैं:

// Typical use of a nonstatic member class
public class MySet<E> extends AbstractSet<E> {
    ... // Bulk of the class omitted

    public Iterator<E> iterator() {
        return new MyIterator();
    }

    private class MyIterator implements Iterator<E> {
        ...
    }
}

आप एक सदस्य वर्ग है कि एक संलग्न उदाहरण के लिए उपयोग की आवश्यकता नहीं है की घोषणा करते हैं, तो हमेशा डाल staticअपनी घोषणा में संशोधक, यह एक nonstatic सदस्य वर्ग एक स्थिर बजाय बना रही है।


1
मुझे पूरा यकीन नहीं है कि यह एडॉप्टर बाहरी वर्ग के MySetउदाहरणों को कैसे बदलता है ? मैं एक पथ-निर्भर प्रकार की तरह कुछ अंतर कर सकता हूं, अर्थात myOneSet.iterator.getClass() != myOtherSet.iterator.getClass(), लेकिन फिर, जावा में यह वास्तव में संभव नहीं है, क्योंकि कक्षा प्रत्येक के लिए समान होगी।
फ्रैंक

@ फ्रेंक यह एक बहुत ही विशिष्ट एडेप्टर वर्ग है - यह आपको सेट को इसके तत्वों (एक Iterator) की एक धारा के रूप में देखने देता है।
user253751 8

@ मिनीबिस धन्यवाद, मैं अच्छी तरह से जानता हूं कि इट्रेटर / एडॉप्टर क्या करता है, लेकिन यह प्रश्न इस बारे में था कि इसे कैसे लागू किया जाता है।
फ्रैंक

@Frank मैं कह रहा था कि यह बाहरी उदाहरण के दृश्य को कैसे बदलता है। ठीक यही एक अडैप्टर करता है - यह आपके द्वारा किसी वस्तु को देखने के तरीके को बदलता है। इस मामले में, वह वस्तु बाहरी उदाहरण है।
user253751

10

आप यह मानने में सही हैं कि गैर-स्थिर आंतरिक कक्षाओं के लिए उपलब्ध विशेषता पहुंच उच्च युग्मन की ओर ले जाती है, इसलिए कोड गुणवत्ता कम होती है, और (गैर-अनाम और गैर-स्थानीय ) आंतरिक कक्षाएं आमतौर पर स्थिर होनी चाहिए।

आंतरिक स्तर को गैर-स्थिर बनाने के निर्णय के डिज़ाइन निहितार्थ जावा पज़लर्स में रखे गए हैं , पहेली 90 (नीचे उद्धरण में बोल्ड फ़ॉन्ट है। मेरा है):

जब भी आप एक सदस्य वर्ग लिखते हैं, तो अपने आप से पूछें, क्या इस वर्ग को वास्तव में एक संलग्न उदाहरण की आवश्यकता है? यदि उत्तर नहीं है, तो इसे बनाएं static। आंतरिक कक्षाएं कभी-कभी उपयोगी होती हैं, लेकिन वे आसानी से जटिलताओं को पेश कर सकते हैं जो समझने के लिए एक कार्यक्रम को कठिन बनाते हैं। जेनरिक (पहेली 89), परावर्तन (पहेली 80), और वंशानुक्रम (यह पहेली) के साथ उनके जटिल संपर्क हैं । यदि आप होने की घोषणा Inner1करते हैं static, तो समस्या दूर हो जाती है। यदि आप भी होने की घोषणा Inner2करते हैं static, तो आप वास्तव में समझ सकते हैं कि कार्यक्रम क्या करता है: वास्तव में एक अच्छा बोनस।

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

यदि आप रुचि रखते हैं, तो स्टैक ओवरफ्लो में इस उत्तर में पहेली 90 का अधिक विस्तृत विश्लेषण प्रदान किया गया है ।


यह ध्यान देने योग्य है कि उपरोक्त अनिवार्य रूप से जावा कक्षाओं और ऑब्जेक्ट्स ट्यूटोरियल में दिए गए मार्गदर्शन का एक विस्तारित संस्करण है :

यदि आपको किसी एन्कोडिंग इंस्टेंस के गैर-सार्वजनिक फ़ील्ड और विधियों तक पहुँच की आवश्यकता है, तो एक गैर-स्थिर नेस्टेड क्लास (या इनर क्लास) का उपयोग करें। यदि आपको इस पहुँच की आवश्यकता नहीं है, तो एक स्थिर नेस्टेड क्लास का उपयोग करें।

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

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


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

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


पूर्णता की खातिर, ऐसे मामले हो सकते हैं जहां ऊपर तर्क लागू नहीं होता है। उदाहरण के लिए, map.keySet javadocs के मेरे पढ़ने के अनुसार , यह सुविधा तंग युग्मन का सुझाव देती है और परिणामस्वरूप, गैर-स्थैतिक वर्गों के खिलाफ तर्क को अमान्य करती है:

Setइस नक्शे में निहित कुंजियों का एक दृश्य लौटाता है । सेट मानचित्र द्वारा समर्थित है, इसलिए मानचित्र में परिवर्तन सेट में प्रतिबिंबित होते हैं, और इसके विपरीत ...

नहीं है कि ऊपर किसी भी तरह आप को बनाए रखने, परीक्षण और डिबग करने के लिए कोड को आसान बना देगा, लेकिन यह कम से कम एक को यह तर्क देने की अनुमति दे सकता है कि जटिलता का मिलान / इच्छित कार्यक्षमता द्वारा उचित है।


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

@ अच्छी तरह से जैसा कि आप देख सकते हैं, ट्यूटोरियल मार्गदर्शन की मेरी व्याख्या (जो कि जावा पज़लर्स द्वारा समर्थित प्रतीत होती है) की तरह है, केवल गैर-स्थिर कक्षाओं का उपयोग करें जब आप यह साबित कर सकें कि यह आवश्यक है, और विशेष रूप से, यह साबित करें कि वैकल्पिक तरीके हैं इससे भी बदतर । टिप्पण लायक है कि मेरे अनुभव में वैकल्पिक तरीकों हमेशा बेहतर निकला
कुटकी

यही तो बात है। अगर यह हमेशा बेहतर होता है, तो हम परेशान क्यों होते हैं?
फ्रैंक

@ एक अन्य उत्तर के लिए मजबूर उदाहरण प्रदान करता है कि यह हमेशा ऐसा नहीं है। Map.keySet javadocs के मेरे पढ़ने के अनुसार , यह सुविधा तंग युग्मन का सुझाव देती है और इसके परिणामस्वरूप, गैर-स्थिर वर्गों के खिलाफ तर्क को अमान्य करता है "सेट मानचित्र द्वारा समर्थित है, इसलिए मानचित्र में परिवर्तन सेट में दिखाई देते हैं, और इसके विपरीत ... "
कुटकी

1

कुछ गलत धारणाएँ:

कम युग्मन: हम आम तौर पर कम युग्मन प्राप्त करते हैं, क्योंकि [स्थिर आंतरिक] वर्ग सीधे अपने बाहरी वर्ग के गुणों तक नहीं पहुँच सकता है।

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

सिंगल क्लास: क्लास लोडर को हर बार एक नए वर्ग का ध्यान रखने की आवश्यकता नहीं होती है, हम बाहरी वर्ग की एक वस्तु बनाते हैं। हमें बस एक ही वर्ग के लिए नई वस्तुएँ मिलती हैं।

मुझे पूरा यकीन नहीं है कि आपका यहां क्या मतलब है। क्लास लोडर कुल में केवल दो बार शामिल होता है, एक बार प्रत्येक क्लास के लिए (जब क्लास लोड होती है)।


जुआ निम्नानुसार है:

जावलैंड में हमें कक्षाएं बनाने में थोड़ा डर लगता है। मुझे लगता है कि इसे एक महत्वपूर्ण अवधारणा की तरह महसूस करना होगा। यह आम तौर पर अपनी फ़ाइल में होता है। इसे महत्वपूर्ण बॉयलरप्लेट की जरूरत है। इसके डिजाइन पर ध्यान देने की जरूरत है।

अन्य भाषाओं - जैसे स्काला, या हो सकता है C ++: एक छोटे धारक (कक्षा, संरचना, जो भी हो) बनाने में बिल्कुल कोई नाटक नहीं है जो कि दो बिट्स डेटा के एक साथ होते हैं। लचीले एक्सेस नियम बॉयलरप्लेट को काटकर आपकी मदद करते हैं, जिससे आप संबंधित कोड को शारीरिक रूप से करीब रख सकते हैं। यह दो वर्गों के बीच आपकी 'माइंड-डिस्टेंस' को कम करता है।

हम आंतरिक वर्ग को निजी रखते हुए प्रत्यक्ष अभिगम पर डिजाइन की चिंताओं को दूर कर सकते हैं।

(... यदि आप पूछते हैं कि 'एक गैर-स्थिर सार्वजनिक आंतरिक वर्ग क्यों?'

आप उन्हें कभी भी उपयोग कर सकते हैं यह कोड पठनीयता और DRY उल्लंघन और बॉयलरप्लेट से बचने में मदद करता है। मेरे पास एक तैयार उदाहरण नहीं है क्योंकि यह सब मेरे अनुभव में अक्सर नहीं होता है, लेकिन ऐसा होता है।


स्थिर आंतरिक कक्षाएं अभी भी एक अच्छा डिफ़ॉल्ट दृष्टिकोण है, btw। गैर-स्थैतिक में एक अतिरिक्त छिपा हुआ क्षेत्र होता है जिसके माध्यम से आंतरिक वर्ग बाहरी को संदर्भित करता है।


0

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

विचार करें:

public class QueryFactory {
    @Inject private Database database;

    public Query create(String sql) {
        return new Query(database, sql);
    }
}

public class Query {
    private final Database database;
    private final String sql;

    public Query(Database database, String sql) {
        this.database = database;
        this.sql = sql;
    } 

    public List performQuery() {
        return database.query(sql);
    }
}

इसके समान इस्तेमाल किया:

Query q = queryFactory.create("SELECT * FROM employees");

q.performQuery();

एक ही चीज एक गैर-स्थिर आंतरिक वर्ग के साथ प्राप्त की जा सकती है:

public class QueryFactory {
    @Inject private Database database;

    public class Query {
        private final String sql;

        public Query(String sql) {
            this.sql = sql;
        }

        public List performQuery() {
            return database.query(sql);
        }
    }
}

इस रूप में उपयोग करें:

Query q = queryFactory.new Query("SELECT * FROM employees");

q.performQuery();

फैक्ट्रियों को विशेष रूप से नामित विधियों, जैसे create, createFromSQLया से लाभ होता है createFromTemplate। वही यहाँ कई आंतरिक कक्षाओं के रूप में भी किया जा सकता है:

public class QueryFactory {

    public class SQL { ... }
    public class Native { ... }
    public class Builder { ... }

}

फैक्ट्री से निर्माण कक्षा तक कम पैरामीटर पासिंग की आवश्यकता होती है, लेकिन पठनीयता थोड़ी कम हो सकती है।

की तुलना करें:

Queries.Native q = queries.new Native("select * from employees");

बनाम

Query q = queryFactory.createNative("select * from employees");
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.