java - अज्ञात वर्ग में केवल अंतिम चर उपलब्ध क्यों हैं?




event-handling anonymous-class (8)

  1. केवल यहां अंतिम हो सकता है। क्यूं कर? मैं इसे निजी सदस्य के रूप में रखने के बिना onClick() विधि में पुन: असाइन कैसे कर सकता हूं?

    private void f(Button b, final int a){
        b.addClickHandler(new ClickHandler() {
    
            @Override
            public void onClick(ClickEvent event) {
                int b = a*5;
    
            }
        });
    }
    
  2. जब मैं क्लिक करता हूं तो मैं 5 * a को कैसे वापस कर सकता हूं? मेरा मतलब,

    private void f(Button b, final int a){
        b.addClickHandler(new ClickHandler() {
    
            @Override
            public void onClick(ClickEvent event) {
                 int b = a*5;
                 return b; // but return type is void 
            }
        });
    }
    

एक अज्ञात वर्ग एक आंतरिक वर्ग है और सख्त नियम आंतरिक कक्षाओं (जेएलएस 8.1.3) पर लागू होता है:

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

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

( जॉन का पूरा जवाब है - मैं इसे एक अवांछित रखता हूं क्योंकि कोई भी जेएलएस नियम में रूचि रख सकता है)


एक चाल है जो अज्ञात वर्ग को बाहरी दायरे में डेटा अपडेट करने की अनुमति देती है।

private void f(Button b, final int a) {
    final int[] res = new int[1];
    b.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
            res[0] = a * 5;
        }
    });

    // But at this point handler is most likely not executed yet!
    // How should we now res[0] is ready?
}

हालांकि, यह चाल सिंक्रनाइज़ेशन समस्याओं के कारण बहुत अच्छी नहीं है। यदि हैंडलर को बाद में बुलाया जाता है, तो आपको 1) की आवश्यकता होती है यदि हैंडलर को विभिन्न थ्रेड से बुलाया गया हो तो res को एक्सेस सिंक्रनाइज़ करें 2) किसी प्रकार का ध्वज या संकेत होना चाहिए जिसे Res

यह चाल ठीक काम करती है, हालांकि, अगर अज्ञात वर्ग को तुरंत उसी धागे में बुलाया जाता है। पसंद:

// ...

final int[] res = new int[1];
Runnable r = new Runnable() { public void run() { res[0] = 123; } };
r.run();
System.out.println(res[0]);

// ...

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


खैर, जावा में, एक चर एक पैरामीटर के रूप में अंतिम नहीं हो सकता है, लेकिन एक वर्ग-स्तर के क्षेत्र के रूप में, जैसे

public class Test
{
 public final int a = 3;

या एक स्थानीय चर के रूप में, जैसे

public static void main(String[] args)
{
 final int a = 3;

यदि आप किसी अज्ञात वर्ग से किसी चर को एक्सेस और संशोधित करना चाहते हैं, तो हो सकता है कि आप चर क्लास को क्लास-लेवल वैरिएबल को संलग्न कक्षा में बनाना चाहें।

public class Test
{
 public int a;
 public void doSomething()
 {
  Runnable runnable =
   new Runnable()
   {
    public void run()
    {
     System.out.println(a);
     a = a+1;
    }
   };
 }
}

आपके पास चर के रूप में चर नहीं हो सकता है और इसे एक नया मान दे सकता है। final मतलब यह है कि: मूल्य अपरिवर्तनीय और अंतिम है।

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

यह सिर्फ आपके अज्ञात वर्ग में एक अंतर्निहित int के मूल्य पर प्रतिलिपि बनाता है।


जैसा कि जॉन के पास कार्यान्वयन विवरण का उत्तर दिया गया है, एक अन्य संभावित उत्तर यह होगा कि जेवीएम रिकॉर्ड में लिखना संभाल नहीं लेता है जिसने अपना सक्रियण समाप्त कर दिया है।

उपयोग के मामले पर विचार करें जहां आवेदन करने की बजाय आपके लैम्ब्डा, किसी स्थान पर संग्रहीत हैं और बाद में चलाए जाते हैं।

मुझे याद है कि जब आप इस तरह के संशोधन करते हैं तो आपको स्मॉलटाक में एक अवैध स्टोर मिल जाएगा।


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

यह मूल रूप से जावा closures के तरीके के कारण है।

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

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

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

यदि आप जावा और सी # बंद होने के बीच अधिक विस्तृत तुलना में रूचि रखते हैं, तो मेरे पास एक article जो इसमें आगे जाता है। मैं इस जवाब में जावा पक्ष पर ध्यान केंद्रित करना चाहता था :)


शायद यह चाल आपको एक विचार देता है

Boolean var= new anonymousClass(){
    private String myVar; //String for example
    @Overriden public Boolean method(int i){
          //use myVar and i
    }
    public String setVar(String var){myVar=var; return this;} //Returns self instane
}.setVar("Hello").method(3);

private void f(Button b, final int a[]) {

    b.addClickHandler(new ClickHandler() {

        @Override
        public void onClick(ClickEvent event) {
            a[0] = a[0] * 5;

        }
    });
}






anonymous-class