2015-09-28 4 views
7

जब मैं जावप के साथ एक enum को अलग करता हूं, तो enum के अंतर्निहित कन्स्ट्रक्टर तर्क गुम होने लगते हैं, और मैं यह नहीं समझ सकता कि क्यों।जवाप के साथ अलग किया गया एनम कन्स्ट्रक्टर तर्क नहीं दिखाता

यहाँ एक enum है:

enum Foo { X } 

मैं संकलन और एकत्रित न इस इस आदेश के साथ (जावा 8u60 पर):

javac Foo.java && javap -c -p Foo 

और यहाँ है उत्पादन मैं:

final class Foo extends java.lang.Enum<Foo> { 
    public static final Foo X; 

    private static final Foo[] $VALUES; 

    public static Foo[] values(); 
    Code: 
     0: getstatic  #1     // Field $VALUES:[LFoo; 
     3: invokevirtual #2     // Method "[LFoo;".clone:()Ljava/lang/Object; 
     6: checkcast  #3     // class "[LFoo;" 
     9: areturn 

    public static Foo valueOf(java.lang.String); 
    Code: 
     0: ldc   #4     // class Foo 
     2: aload_0 
     3: invokestatic #5     // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; 
     6: checkcast  #4     // class Foo 
     9: areturn 

    private Foo(); // <--- here 
    Code: 
     0: aload_0 
     1: aload_1 
     2: iload_2 
     3: invokespecial #6     // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V 
     6: return 

    static {}; 
    Code: 
     0: new   #4     // class Foo 
     3: dup 
     4: ldc   #7     // String X 
     6: iconst_0 
     7: invokespecial #8     // Method "<init>":(Ljava/lang/String;I)V 
     10: putstatic  #9     // Field X:LFoo; 
     13: iconst_1 
     14: anewarray  #4     // class Foo 
     17: dup 
     18: iconst_0 
     19: getstatic  #9     // Field X:LFoo; 
     22: aastore 
     23: putstatic  #1     // Field $VALUES:[LFoo; 
     26: return 
} 

मेरा भ्रम निजी कन्स्ट्रक्टर के साथ होता है जो प्रत्येक enum स्थिरता को तुरंत चालू करने के लिए उपयोग किया जाता है। Disassembly दिखाता है कि यह कोई तर्क नहीं लेता है (private Foo();), लेकिन यह निश्चित रूप से तर्क लेता है। उदाहरण के लिए, आप load पास किए गए एनम निरंतर नाम और क्रमिक, साथ ही साथ this पॉइंटर पढ़ने के निर्देशों को देख सकते हैं, और उन्हें the superclass constructor पर भेज सकते हैं, जिसके लिए उन्हें आवश्यकता होती है। स्थिर प्रारंभकर्ता ब्लॉक में कोड यह भी दिखाता है कि यह कन्स्ट्रक्टर को कॉल करने से पहले उन तर्कों को स्टैक पर धक्का देता है।

अब मैं मान लिया होता तो यह सिर्फ javap में एक अस्पष्ट बग था, लेकिन जब मैं ग्रहण के संकलक के साथ ठीक उसी enum संकलन और कि javap का उपयोग कर एकत्रित न, निर्माता तर्क से पता चला रहे हैं को छोड़कर बिल्कुल वैसा ही है:

final class Foo extends java.lang.Enum<Foo> { 
    public static final Foo X; 

    private static final Foo[] ENUM$VALUES; 

    static {}; 
    Code: 
     0: new   #1     // class Foo 
     3: dup 
     4: ldc   #12     // String X 
     6: iconst_0 
     7: invokespecial #13     // Method "<init>":(Ljava/lang/String;I)V 
     10: putstatic  #17     // Field X:LFoo; 
     13: iconst_1 
     14: anewarray  #1     // class Foo 
     17: dup 
     18: iconst_0 
     19: getstatic  #17     // Field X:LFoo; 
     22: aastore 
     23: putstatic  #19     // Field ENUM$VALUES:[LFoo; 
     26: return 

    private Foo(java.lang.String, int); // <--- here 
    Code: 
     0: aload_0 
     1: aload_1 
     2: iload_2 
     3: invokespecial #23     // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V 
     6: return 

    public static Foo[] values(); 
    Code: 
     0: getstatic  #19     // Field ENUM$VALUES:[LFoo; 
     3: dup 
     4: astore_0 
     5: iconst_0 
     6: aload_0 
     7: arraylength 
     8: dup 
     9: istore_1 
     10: anewarray  #1     // class Foo 
     13: dup 
     14: astore_2 
     15: iconst_0 
     16: iload_1 
     17: invokestatic #27     // Method java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V 
     20: aload_2 
     21: areturn 

    public static Foo valueOf(java.lang.String); 
    Code: 
     0: ldc   #1     // class Foo 
     2: aload_0 
     3: invokestatic #35     // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; 
     6: checkcast  #1     // class Foo 
     9: areturn 
} 

मेरा प्रश्न है: क्या शारीरिक रूप से एक javac संकलित enum और एक एक्लिप्स संकलित enum कि javac संकलित enum के लिए निर्माता तर्क नहीं दिखाने के लिए javap का कारण बनता है के बीच अलग है? और क्या यह एक बग है (जावप में, जावैक में, या ग्रहण में)?

+0

अनुमान में, '-g' ध्वज की सेटिंग (डीबगिंग जानकारी की नियंत्रण जनरेशन)। – CPerkins

+0

@CPerkins अच्छी परिकल्पना लेकिन मैंने '-g' की तुलना की (सभी डीबगिंग जानकारी उत्पन्न करें) और '-g: none' (कोई डिबगिंग जानकारी उत्पन्न नहीं करें) और ऐसा कोई फर्क नहीं पड़ता। – Boann

+0

क्या आपने '-v' (verbose) ध्वज को आजमाया था? इसे कम से कम आपको कन्स्ट्रक्टर का वर्णनकर्ता दिखाना चाहिए। मेरा अनुमान है कि 'जावैक' पहले पैरामीटर 'मंडेटेड' को चिह्नित करता है, जो उन्हें 'जावप' को छोड़ सकता है। – Clashsoft

उत्तर

3

कक्षा फ़ाइल के अंदर एक विधि के पैरामीटर और वापसी प्रकार को method descriptor द्वारा वर्णित किया गया है।

1.5 में जेनरिक के परिचय के साथ। अतिरिक्त जानकारी कक्षा फ़ाइल प्रारूप में पेश की गई थी, method signature

"विधि वर्णनकर्ता" का उपयोग टाइप एरर के बाद विधि का वर्णन करने के लिए किया जाता है, "विधि हस्ताक्षर" अतिरिक्त रूप से सामान्य प्रकार की जानकारी होती है।

अब javap विधि हस्ताक्षर प्रिंट करता है (जिसमें अधिक जानकारी होती है), और जब -v ध्वज सेट किया गया है, तो यह वर्णनकर्ता को भी प्रिंट करता है।

यह बताता है कि javac जेनरेट एनम क्लास के निर्माता भी पैरामीटर प्रकार String और int के साथ एक विधि वर्णनकर्ता है। अब यह भी स्पष्ट है कि एलिप्स और जावैक दोनों कोड कोड उत्पन्न क्यों करते हैं। दोनों निजी कन्स्ट्रक्टर को तर्क String और int के साथ कॉल करते हैं।

अभी भी क्या समझाया जाना चाहिए: javac एक हस्ताक्षर क्यों बनाते हैं जो वर्णनकर्ता से अलग है - कोई जेनेरिक शामिल नहीं है?

वैसे भी, enum निर्माता के बारे में javac के व्यवहार other troubles कारण बना हुआ है और javac के लिए एक बग रिपोर्ट filed था:

एक enum घोषणा के निर्माता के लिए कोई ज़रूरत नहीं एक हस्ताक्षर विशेषता के लिए एक विधि के संचय नहीं है हस्ताक्षर अगर 1) कन्स्ट्रक्टर सामान्य नहीं है और 2) इसके औपचारिक पैरामीटर प्रकार न तो पैरामीटरयुक्त प्रकार हैं और न ही चर टाइप करें। यह एक बग है यदि जैवैक ऊपर लिखे गए कन्स्ट्रक्टर के लिए हस्ताक्षर विशेषता की अपेक्षा करता है।

निम्नलिखित टिप्पणियां और मामले के वर्गीकरण से पता चलता है कि यह javac में एक वास्तविक बग है।

+0

धन्यवाद, यह अंतर बताता है। यह पता चला है कि जावैक में हस्ताक्षर में स्पष्ट रूप से * परिभाषित एनम कन्स्ट्रक्टर पैरामीटर शामिल हैं, केवल निरंतर नाम और औपचारिक के लिए दो अंतर्निहित नहीं हैं। मुझे विश्वास नहीं है कि यह जावैक में एक बग है, लेकिन वीएम स्पेक में कमी की अधिकता है। हस्ताक्षर विशेषता के लिए वीएम स्पेक के नियम, जब इसे उपस्थित होना चाहिए और इसमें क्या शामिल होना चाहिए, तो इसे खतरनाक रूप से परिभाषित किया जाता है। – Boann

+0

इसी तरह की समस्या आंतरिक कक्षाओं के रचनाकारों के साथ है, जिसमें बाहरी वर्ग के संदर्भ को पारित करने के लिए उपयोग किया गया एक छुपा पैरामीटर है। उदाहरण के लिए: 'कक्षा बाहरी {कक्षा आंतरिक {आंतरिक() {}}} '- कन्स्ट्रक्टर डिस्सेप्लोर में दिखाई देता है:' बाहरी $ आंतरिक (बाहरी);'। लेकिन अगर कन्स्ट्रक्टर * जेनेरिक * बनाया जाता है: 'क्लास ऑउटर {क्लास इनर { इनर() {}}}', यह क्लासफ़ाइल में हस्ताक्षर विशेषता प्राप्त करता है और जावप इसे ' बाहरी $ इनर(); '- * पैरामीटर अचानक अदृश्य है *, और यह जैवैक और ईसीजे दोनों के साथ सच है। अजीब चीजें! कम से कम '-v' विकल्प सच बताता है! – Boann

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