2010-05-10 12 views
20

जावा में एक अनाम आंतरिक कक्षा इसके स्थानीय दायरे में चरों को संदर्भित कर सकती है:जावा कैसे आंतरिक कक्षा बंद करने को लागू करता है?

public class A { 
    public void method() { 
     final int i = 0; 

     doStuff(new Action() { 
      public void doAction() { 
       Console.printf(i); // or whatever 
      } 
     }); 
    } 
} 

मेरा प्रश्न यह है कि यह वास्तव में कैसे कार्यान्वित किया जाता है? Xzx18 अज्ञात आंतरिक doAction कार्यान्वयन कैसे प्राप्त करता है, और यह final क्यों होना चाहिए?

उत्तर

11

संकलक स्वचालित रूप से आपके गुमनाम भीतरी वर्ग के लिए एक निर्माता उत्पन्न करता है, और इस निर्माता में अपने स्थानीय चर गुजरता है।

कन्स्ट्रक्टर इस मान को क्लास वेरिएबल (एक फ़ील्ड) में सहेजता है, जिसे i भी नाम दिया जाता है, जिसका उपयोग "बंद" के अंदर किया जाएगा।

यह अंतिम क्यों होना चाहिए? खैर के दशक में स्थिति का पता लगाने में जहां यह नहीं है करते हैं:

public class A { 
    public void method() { 
     int i = 0; // note: this is WRONG code 

     doStuff(new Action() { 
      public void doAction() { 
       Console.printf(i); // or whatever 
      } 
     }); 

     i = 4; // A 
     // B 
     i = 5; // C 
    } 
} 

स्थिति एक फ़ील्ड में Action की i भी बदलने की आवश्यकता, मान लेते हैं यह संभव है करते हैं: यह Action वस्तु के संदर्भ में की जरूरत है।

मान लें कि स्थिति बी में Action के इस उदाहरण कचरा-एकत्र किया जाता है।

स्थिति सी में अब: यह यह की कक्षा चर अद्यतन करने के लिए Action का एक उदाहरण जरूरत है, लेकिन मूल्य GCed है। इसे जीसीड के "जानना" चाहिए, लेकिन यह मुश्किल है।

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

+0

कर सकता हूं तो दोनों उत्तरों को स्वीकार करेंगे, दरअसल, संश्लेषित चर जो परिवर्तनीय की एक प्रति रखता है, नाम नहीं है I। आप जिस कंपाइलर का उपयोग कर रहे हैं उसके आधार पर यह "$ i" या "+ i" है। –

15

स्थानीय चर (स्पष्ट रूप से) method() और doAction() जैसी विभिन्न विधियों के बीच साझा नहीं किए गए हैं। लेकिन चूंकि यह अंतिम है, इस मामले में कुछ भी "बुरा" नहीं हो सकता है, इसलिए भाषा अभी भी इसकी अनुमति देती है। संकलक हालांकि, स्थिति के बारे में कुछ चालाक करने की जरूरत है। क्या javac पैदा करता है पर एक नजर है की सुविधा देता है:

$ javap -v "A\$1"   # A$1 is the anonymous Action-class. 
... 
final int val$i; // A field to store the i-value in. 

final A this$0;  // A reference to the "enclosing" A-object. 

A$1(A, int); // created constructor of the anonymous class 
    Code: 
    Stack=2, Locals=3, Args_size=3 
    0: aload_0 
    1: aload_1 
    2: putfield #1; //Field this$0:LA; 
    5: aload_0 
    6: iload_2 
    7: putfield #2; //Field val$i:I 
    10: aload_0 
    11: invokespecial #3; //Method java/lang/Object."<init>":()V 
    14: return 
    ... 
public void doAction(); 
    Code: 
    Stack=2, Locals=1, Args_size=1 
    0: getstatiC#4; //Field java/lang/System.out:Ljava/io/PrintStream; 
    3: aload_0 
    4: getfield #2; //Field val$i:I 
    7: invokevirtual #5; //Method java/io/PrintStream.println:(I)V 
    10: return 

यह वास्तव में पता चलता है कि यह

  • मैदान में i चर कर दिया,
  • गुमनाम वर्ग के लिए एक निर्माता है, जो एक संदर्भ स्वीकार किए जाते हैं बनाया A ऑब्जेक्ट
  • पर जिसे बाद में doAction() विधि में एक्सेस किया गया।

(ए साइड ध्यान दें:। मैं new java.util.Random().nextInt() चर प्रारंभ करने में इसे दूर कोड का एक बहुत कुछ के अनुकूलन से रोकने के लिए किया था)


इसी प्रकार यहां चर्चा

method local innerclasses accessing the local variables of the method

+2

इसमें थ्रेडिंग के साथ कुछ भी नहीं है (बहुत)। यह केवल एक दुष्प्रभाव है। अच्छा जावा विवाद, बीटीडब्ल्यू: संकलक में कुछ महान अंतर्दृष्टि देता है। – Pindatjuh

+0

आप सही हैं। मैं संशोधित करूंगा। सूचक के लिए धन्यवाद। – aioobe

+0

@Pindatjuh, विवाद को अपडेट किया गया ... एहसास हुआ कि यह बहुत सारे कोड को अनुकूलित करता है क्योंकि संकलक महसूस करता है 'i' हमेशा होता था 0. – aioobe

3

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

अधिक जानकारी के लिए Java Final - an enduring mystery देखें।

संबंधित मुद्दे