2011-08-16 15 views
14

मैं प्रतिबिंब का उपयोग करके अज्ञात वर्गों के उदाहरण बनाने की कोशिश कर रहा हूं। लेकिन आकस्मिक रूप से मैंने तत्काल व्यवहार के दौरान अजीब व्यवहार देखा है।अनाम वर्ग भ्रम का गतिशील निर्माण

कृपया,

public class HideAndSeek { 

    @SuppressWarnings("unchecked") 
    public static void main(String[] args) throws IllegalAccessException, InstantiationException{ 

     final String finalString = "I'm final :)"; 

     Object object2 = new Object(){ 

      { 
       System.out.println("Instance initializing block"); 
       System.out.println(finalString); 
      }   

      private void hiddenMethod() { 
       System.out.println("Use reflection to find me :)"); 
      } 
     }; 

     Object tmp = object2.getClass().newInstance(); 
    } 

} 

इस कोड को अच्छी तरह से काम करता है कोड के इन समान टुकड़े को देखें, और उत्पादन की उम्मीद

Instance initializing block 
I'm final :) 
Instance initializing block 
I'm final :) 

इस मैं सरल तरीके से कोड बदलने का फैसला किया गया है के बाद (बस अतिरिक्त java.util.Calendar)

import java.util.Calendar; 

    public class HideAndSeek { 

     @SuppressWarnings("unchecked") 
     public static void main(String[] args) throws IllegalAccessException, InstantiationException{ 

      final String finalString = "I'm final :)"; 

      final Calendar calendar = Calendar.getInstance(); 
      System.out.println(calendar.getTime().toString()); //works well 

      Object object2 = new Object(){ 

       { 
        System.out.println("Instance initializing block"); 
        System.out.println(finalString); 

        //simply added this line 
        System.out.println(calendar.getTime().toString()); 
       }   

       private void hiddenMethod() { 
        System.out.println("Use reflection to find me :)"); 
       } 
      }; 

      Object tmp = object2.getClass().newInstance(); 
     } 

    } 

और यहाँ उत्पादन है कि मुझे मिल गया है है:

Wed Aug 17 02:08:47 EEST 2011 
Instance initializing block 
I'm final :) 
Wed Aug 17 02:08:47 EEST 2011 
Exception in thread "main" java.lang.InstantiationException: HideAndSeek$1 
    at java.lang.Class.newInstance0(Unknown Source) 
    at java.lang.Class.newInstance(Unknown Source) 
    at HideAndSeek.main(HideAndSeek.java:29) 

जैसा कि आप देख सकते हैं - नया उदाहरण नहीं बनाया गया है।

क्या कोई मुझे बता सकता है, ऐसे परिवर्तनों का कारण?

धन्यवाद

+0

क्या होगा यदि कैलेंडर इंस्टेंस अंतिम नहीं था? – SJuan76

+0

@ SJuan76। ... तो आप इसे अज्ञात आंतरिक कक्षा में पारित करने में सक्षम नहीं होते। –

उत्तर

17

यह एक बहुत ही जटिल उत्तर के साथ एक बहुत ही सरल सवाल है। कृपया मुझे इसके साथ सहन करें क्योंकि मैं इसे समझाने की कोशिश करता हूं।

(मुझे यकीन है कि क्यों अपने स्टैक ट्रेस Class में लाइन नंबर नहीं दे करता है नहीं कर रहा हूँ) स्रोत कोड जहां अपवाद Class में उठाया है को देखते हुए:

try 
{ 
    Class[] empty = {}; 
    final Constructor<T> c = getConstructor0(empty, Member.DECLARED); 
    // removed some code that was not relevant 
} 
catch (NoSuchMethodException e) 
{ 
    throw new InstantiationException(getName()); 
} 

आप देखते हैं कि NoSuchMethodException किया जा रहा है InstantiationException के रूप में पुनर्स्थापित। इसका मतलब है कि कक्षा प्रकार object2 के लिए कोई गैर-तर्क निर्माता नहीं है।

पहला, object2 किस प्रकार का है? कोड

System.out.println("object2 class: " + object2.getClass()); 

के साथ हम देखते हैं कि

object2 वर्ग: कक्षा junk.NewMain $ 1

जो सही है (मैं, पैकेज कबाड़ में नमूना कोड को चलाने के वर्ग NewMain)।

junk.NewMain$1 के निर्माता क्या हैं?

Class obj2Class = object2.getClass(); 
try 
{ 
    Constructor[] ctors = obj2Class.getDeclaredConstructors(); 
    for (Constructor cc : ctors) 
    { 
    System.out.println("my ctor is " + cc.toString()); 
    } 
} 
catch (Exception ex) 
{ 
    ex.printStackTrace(); 
} 

जो हमें

मेरी ctor देता है $ 1 (java.util.Calendar) junk.NewMain है

तो अपने गुमनाम वर्ग एक Calendar की तलाश में है में पारित किया जाना है। यह आपके लिए काम करेगा:

Object newObj = ctors[0].newInstance(Calendar.getInstance()); 

यदि आपके पास कुछ पसंद है ई इस:

final String finalString = "I'm final :)"; 
final Integer finalInteger = new Integer(30); 
final Calendar calendar = Calendar.getInstance(); 
Object object2 = new Object() 
{ 
    { 
    System.out.println("Instance initializing block"); 
    System.out.println(finalString); 
    System.out.println("My integer is " + finalInteger); 
    System.out.println(calendar.getTime().toString()); 
    } 
    private void hiddenMethod() 
    { 
    System.out.println("Use reflection to find me :)"); 
    } 
}; 

तो newInstance करने के लिए अपने कॉल क्योंकि वहाँ, ctor में पर्याप्त तर्क नहीं हैं, क्योंकि अब यह चाहता है काम नहीं करेगा:

मेरी ctor junk.NewMain $ 1 (जावा है। lang.Integer, java.util.Calendar)

मैं तो दृष्टांत तो उस के साथ

Object newObj = ctors[0].newInstance(new Integer(25), Calendar.getInstance()); 

डीबगर का उपयोग करने के अंदर एक झलक दिखाता है कि finalInteger 25 है और अंतिम मान 30 नहीं है।

चीजें थोड़ा जटिल हैं क्योंकि आप उपरोक्त सभी को एक स्थिर संदर्भ में कर रहे हैं। आप ऊपर अपने सभी कोड लेने के लिए और एक गैर स्थैतिक विधि में यह इतना की तरह ले जाते हैं (याद रखें, मेरी कक्षा junk.NewMain है):

public static void main(String[] args) 
{ 
    NewMain nm = new NewMain(); 
    nm.doIt(); 
} 

public void doIt() 
{ 
    final String finalString = "I'm final :)"; 
    // etc etc 
} 

आप अपने भीतर के वर्ग के लिए ctor मिल जाएगा अब है (को हटाने मेरी जोड़ा पूर्णांक संदर्भ):

मेरी ctor junk.NewMain $ 1 है (junk.NewMain, java.util.Calendar)

Java Language Specification, खंड 15.9.3 यह इस तरह से बताते हैं:

तो सी एक अनाम वर्ग, और सी, एस के प्रत्यक्ष सुपर क्लास है, है एक भीतरी वर्ग है, तो: एस एक स्थानीय वर्ग है और एस एक स्थिर संदर्भ में होता है तो

  • हैं तर्क सूची में तर्क, यदि कोई हो, तो अभिव्यक्ति में दिखाई देने वाले क्रम में कन्स्ट्रक्टर के लिए तर्क हैं।
  • अन्यथा, तुरंत enclosing एस के संबंध में मैं के कहने निर्माता को पहले तर्क, वर्ग उदाहरण निर्माण अभिव्यक्ति की तर्क सूची में तर्क के बाद, यदि कोई हो, ताकि वे में प्रदर्शित में है भाव।

गुमनाम निर्माता सब पर बहस क्यों लगता है?

चूंकि आप किसी अज्ञात आंतरिक कक्षा के लिए कोई कन्स्ट्रक्टर नहीं बना सकते हैं, उदाहरण प्रारंभकर्ता ब्लॉक उस उद्देश्य को पूरा करता है (याद रखें, आपके पास केवल उस अज्ञात आंतरिक वर्ग का एक उदाहरण है)। वीएम को आंतरिक वर्ग का कोई ज्ञान नहीं है क्योंकि संकलक व्यक्तिगत वर्गों के रूप में सबकुछ अलग करता है (उदाहरण के लिए जंक। न्यूमैन $ 1)। उस वर्ग के लिए ctor उदाहरण प्रारंभकर्ता की सामग्री शामिल है।

यह JLS 15.9.5.1 Anonymous Constructors द्वारा explayed है:

... अनाम निर्माता वर्ग उदाहरण निर्माण अभिव्यक्ति है जिसमें सी घोषित है करने के लिए प्रत्येक वास्तविक तर्क के लिए एक औपचारिक पैरामीटर है।

आपके इंस्टेंस प्रारंभकर्ता के पास Calendar ऑब्जेक्ट का संदर्भ है। कंपाइलर को उस आंतरिक समय में उस रनटाइम मान को और कैसे प्राप्त किया जा सकता है (जिसे केवल वीएम के लिए एक वर्ग के रूप में बनाया गया है) कन्स्ट्रक्टर के माध्यम से छोड़कर?

अंत में (यय), अंतिम जलने वाले प्रश्न का उत्तर। कन्स्ट्रक्टर को String क्यों नहीं चाहिए? JLS 3.10.5 के अंतिम बिट बताते हैं कि:

स्ट्रिंग्स निरंतर भाव द्वारा गणना संकलन समय पर गणना की जाती है और फिर इलाज किया रूप में यदि वे शाब्दिक थे।

दूसरे शब्दों में, अपने String मूल्य संकलन समय पर जाना जाता है, क्योंकि यह तो यह गुमनाम निर्माता का हिस्सा बनने की जरूरत नहीं है एक शाब्दिक है है। साबित करने के लिए यह मामला है हम JLS 3.10.5 में अगले बयान परीक्षण करेंगे:

स्ट्रिंग्स रन टाइम पर संयोजन द्वारा गणना नव निर्मित और इसलिए अलग कर रहे हैं।

thusly अपने कोड में परिवर्तित करें:

String str1 = "I'm"; 
String str2 = " final!"; 
final String finalString = str1 + str2 

और आप (गैर स्थिर संदर्भ में) मिल जाएगा अपने ctor अब है:

मेरी ctor है junk.NewMain $ 1 (जंक। न्यूमेन, जावा.लंग.स्ट्रिंग, जावा.यूटिल। कैलेंडर)

पुhew। मुझे उम्मीद है कि यह समझ में आता है और सहायक था। मैंने बहुत कुछ सीखा, यह निश्चित रूप से है!

+0

ब्रावो! अच्छी तरह से शोध किया। –

+0

+37 (अगर मैं कर सकता था): महान जवाब! –

+0

@ पॉल बहुत जानकारीपूर्ण उत्तर के लिए धन्यवाद :) – stemm

4

क्योंकि दूसरे मामले में अब कोई डिफ़ॉल्ट कन्स्ट्रक्टर नहीं है।

पहले मामले में अंतिम स्ट्रिंग को रेखांकित किया जाता है क्योंकि यह स्थिर है।

दूसरे मामले में अज्ञात आंतरिक वर्ग को राज्य को कैप्चर करने के लिए कैलेंडर के उदाहरण को अपने कन्स्ट्रक्टर में स्वीकार करना होगा। आप आसानी से पुष्टि कर सकते हैं कि यह कर रहा है:

Object tmp = object2.getClass().getDeclaredConstructor(Calendar.class).newInstance(calendar); 
संबंधित मुद्दे