119

autoboxing क्यों इस फेंक NullPointerExceptionBooleans, सशर्त ऑपरेटरों और

public static void main(String[] args) throws Exception { 
    Boolean b = true ? returnsNull() : false; // NPE on this line. 
    System.out.println(b); 
} 

public static Boolean returnsNull() { 
    return null; 
} 

जबकि इस

public static void main(String[] args) throws Exception { 
    Boolean b = true ? null : false; 
    System.out.println(b); // null 
} 

नहीं करता है?

समाधान Boolean.FALSE द्वारा false को बदलने के लिए boolean --which संभव नहीं है करने के लिए जा रहा है अनबॉक्स्ड null से बचने के लिए जिस तरह से है। लेकिन यह सवाल नहीं है। प्रश्न है क्यों? क्या जेएलएस में कोई संदर्भ है जो इस व्यवहार की पुष्टि करता है, खासकर दूसरे मामले में?

+27

वाह, ऑटोबॉक्सिंग एक अंतहीन स्रोत है ... er ... जावा प्रोग्रामर के लिए आश्चर्य, है ना? :-) – leonbloy

+0

मुझे एक ही समस्या थी और मुझे क्या आश्चर्य हुआ कि यह ओपनजेडीके वीएम पर असफल रहा लेकिन हॉटस्पॉट वीएम पर काम किया ... एक बार लिखें, कहीं भी चलाएं! – kodu

उत्तर

85

अंतर यह है कि returnsNull() विधि का स्पष्ट प्रकार संकलन समय पर अभिव्यक्ति की स्थिर टाइपिंग को प्रभावित करता है:

E1: `true ? returnsNull() : false` - boolean (auto-unboxing 2nd operand to boolean) 

E2: `true ? null : false` - Boolean (autoboxing of 3rd operand to Boolean) 

देखें जावा भाषा विशिष्टता, खंड 15.25 Conditional Operator ? :

  • ई 1 के लिए, दूसरे और तीसरे ऑपरेटरों के प्रकारहैंऔर boolean क्रमशः, इसलिए इस खंड लागू होता है:

    दूसरे और तीसरे ऑपरेंड में से एक बूलियन प्रकार का है और अन्य प्रकार के प्रकार बूलियन की है, तो सशर्त अभिव्यक्ति के प्रकार बूलियन है।

    के बाद से अभिव्यक्ति के प्रकार boolean है, 2 संकार्य boolean लिए मजबूर किया जाना चाहिए। कंपाइलर दूसरे ऑपरेंड (returnsNull() का रिटर्न वैल्यू) में ऑटो-अनबॉक्सिंग कोड डालने के लिए boolean टाइप करता है। यह निश्चित रूप से null से एनपीई रन-टाइम पर लौटाता है।

  • E2 के लिए, 2 के प्रकार और 3 ऑपरेंड <special null type> (! E1 में के रूप में नहीं Boolean) और boolean क्रमशः हैं, इसलिए कोई विशेष टाइपिंग खंड लागू होता है (go read 'em!), इसलिए अंतिम "अन्यथा" खंड लागू होता है:

    अन्यथा, दूसरे और तीसरे ऑपरेशंस क्रमशः एस 1 और एस 2 प्रकार हैं। टी 1 को उस प्रकार का होना चाहिए जो मुक्केबाजी रूपांतरण को एस 1 में लागू करने से नतीजा है, और टी 2 को उस प्रकार का होना चाहिए जो मुक्केबाजी रूपांतरण को एस 2 में लागू करने के परिणामस्वरूप हो। सशर्त अभिव्यक्ति का प्रकार कैप्चर रूपांतरण (§5.1.10) को लब (टी 1, टी 2) (§15.12.2.7) पर लागू करने का परिणाम है।

    • एस 1 == <special null type> (§4.1 देखें)
    • एस 2 == boolean
    • टी 1 == बॉक्स (एस 1) == <special null type> (§5.1.7 में मुक्केबाजी रूपांतरण की सूची में अंतिम आइटम देखें)
    • टी 2 == बॉक्स (S2) == `बूलियन
    • लब (T1, T2) == Boolean

    तो सशर्त अभिव्यक्ति का प्रकार Boolean है और तीसरा ऑपरेंड Boolean पर ले जाया जाना चाहिए। कंपाइलर तीसरे ऑपरेंड (false) के लिए ऑटो-मुक्केबाजी कोड सम्मिलित करता है। दूसरे ऑपरेंड को E1 में ऑटो-अनबॉक्सिंग की आवश्यकता नहीं है, इसलिए null पर कोई ऑटो-अनबॉक्सिंग एनपीई वापस नहीं किया जाता है।


यह सवाल एक समान प्रकार के विश्लेषण की जरूरत है:

Java conditional operator ?: result type

+4

समझ में आता है ... मुझे लगता है। [§15.12.2.7] (http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#341287) एक दर्द है। – BalusC

+0

यह आसान है ... लेकिन केवल हिंडसाइट में। :-) –

+0

@BertF 'lub (T1, T2) 'में' lub' 'फ़ंक्शन क्या है? – Geek

21

लाइन: unboxing प्रदर्शन करने के लिए

Boolean b = true ? returnsNull().getBoolean() : false; 

;:

Boolean b = true ? returnsNull() : false; 

आंतरिक रूप से करने के लिए तब्दील हो जाता है इस प्रकार: null.getBoolean() एक एनपीई

ऑटोबॉक्सिंग का उपयोग करते समय यह प्रमुख नुकसान में से एक है। यह behavious वास्तव में 5.1.8 JLS

संपादित प्रलेखित है: मेरा मानना ​​है कि unboxing तीसरे ऑपरेटर बूलियन प्रकार की जा रही है की वजह से है, जैसे (अंतर्निहित डाली जोड़ा):

Boolean b = (Boolean) true ? true : false; 
+1

ऐसा क्यों अनबॉक्स करने का प्रयास करता है, जब परम मूल्य एक बूलियन ऑब्जेक्ट होता है? –

15

Java Language Specification, section 15.25 से:

  • यदि दूसरे और तीसरे ऑपरेटरों में से एक प्रकार का बूलियन है और अन्य प्रकार का प्रकार बूलियन, है तो वें सशर्त अभिव्यक्ति का ई प्रकार बूलियन है।

तो, पहला उदाहरण आदेश पहला नियम के अनुसार boolean को Boolean कन्वर्ट करने के लिए में Boolean.booleanValue() कॉल करने के लिए कोशिश करता है।

दूसरे मामले पहले संकार्य अशक्त प्रकार का है, जब दूसरा इसलिए autoboxing रूपांतरण लागू किया जाता है, संदर्भ प्रकार की नहीं है:

  • अन्यथा, दूसरे और तीसरे ऑपरेंड हैं क्रमशः एस 1 और एस 2 प्रकार। टी 1 को को बॉक्सिंग एस 1 में रूपांतरण लागू करने से परिणाम दें, और टी 2 को टाइप करें जो कि मुक्केबाजी एस 2 में रूपांतरण लागू करने से परिणाम देता है। सशर्त अभिव्यक्ति का प्रकार परिणाम कैप्चर रूपांतरण (§5.1.10) को लुब (टी 1, टी 2) (§15.12.2.7) पर लागू करने का परिणाम है।
+0

यह पहला मामला जवाब देता है, लेकिन दूसरा मामला नहीं। – BalusC

+0

संभवतः एक अपवाद है जब मानों में से एक 'शून्य' है। –

+0

@Erick: क्या जेएलएस इसकी पुष्टि करता है? – BalusC

0

हम बाइट कोड से इस समस्या को देख सकते हैं। मुख्य बाइट कोड के लाइन 3 पर, 3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z, मूल्य शून्य के बॉक्सिंग बूलियन, invokevirtual विधि java.lang.Boolean.booleanValue विधि, यह निश्चित रूप से एनपीई फेंक देगा।

public static void main(java.lang.String[]) throws java.lang.Exception; 
     descriptor: ([Ljava/lang/String;)V 
     flags: ACC_PUBLIC, ACC_STATIC 
     Code: 
     stack=2, locals=2, args_size=1 
      0: invokestatic #2     // Method returnsNull:()Ljava/lang/Boolean; 
      3: invokevirtual #3     // Method java/lang/Boolean.booleanValue:()Z 
      6: invokestatic #4     // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean; 
      9: astore_1 
      10: getstatic  #5     // Field java/lang/System.out:Ljava/io/PrintStream; 
      13: aload_1 
      14: invokevirtual #6     // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 
      17: return 
     LineNumberTable: 
      line 3: 0 
      line 4: 10 
      line 5: 17 
     Exceptions: 
     throws java.lang.Exception 

    public static java.lang.Boolean returnsNull(); 
     descriptor:()Ljava/lang/Boolean; 
     flags: ACC_PUBLIC, ACC_STATIC 
     Code: 
     stack=1, locals=0, args_size=0 
      0: aconst_null 
      1: areturn 
     LineNumberTable: 
      line 8: 0 
संबंधित मुद्दे