बूलियन, सशर्त ऑपरेटर और ऑटोबॉक्सिंग


132

यह क्यों फेंकता है? NullPointerException

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
}

?

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


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

मुझे इसी तरह की समस्या थी और मुझे क्या फायदा हुआ कि यह OpenJDK VM पर विफल रही, लेकिन HotSpot VM पर काम किया ... एक बार लिखें, कहीं भी दौड़ें!
कोडु डे

जवाबों:


92

अंतर यह है कि स्पष्ट प्रकार की 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 सशर्त संचालक देखें? :

  • E1 के लिए, दूसरे और तीसरे ऑपरेंड के प्रकार क्रमशः Booleanऔर booleanइसलिए यह क्लॉज लागू होता है:

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

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

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

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

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

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


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

जावा सशर्त ऑपरेटर?: परिणाम प्रकार


4
समझ में आता है ... मुझे लगता है। §15.12.2.7 एक दर्द है।
बालूसी

यह आसान है ... लेकिन केवल अड़चन में। :-)
बर्ट एफ

@BertF स्टैंड lubमें क्या कार्य करता है lub(T1,T2)?
गीक

1
@ गीक - लब () - कम से कम ऊपरी बाउंड - मूल रूप से निकटतम सुपरक्लास जो उनके पास आम है; चूँकि नल (प्रकार "विशेष नल प्रकार") को किसी भी प्रकार से परिवर्तित (चौड़ा) किया जा सकता है, आप विशेष नल प्रकार को लुब () के प्रयोजनों के लिए किसी भी प्रकार (वर्ग) का "सुपरक्लास" मान सकते हैं।
बर्ट एफ

25

रेखा:

    Boolean b = true ? returnsNull() : false;

आंतरिक रूप से रूपांतरित हो जाता है:

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

अनबॉक्सिंग करने के लिए; इस प्रकार: null.booleanValue()एक NPE निकलेगा

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

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

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

2
यह उस तरह से अनबॉक्स करने का प्रयास क्यों करता है, जब अंतिम मूल्य एक बूलियन वस्तु है?
एरिक रॉबर्टसन

16

से जावा भाषा विशिष्टता, खंड 15.25 :

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

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

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

  • अन्यथा, दूसरे और तीसरे ऑपरेंड क्रमशः S1 और S2 के प्रकार हैं। बता दें कि टी 1 वह प्रकार है जो बॉक्सिंग रूपांतरण को एस 1 में लागू करने से होता है, और टी 2 उस प्रकार का होता है जो बॉक्सिंग रूपांतरण को एस 2 में लागू करने से होता है। सशर्त अभिव्यक्ति का प्रकार लब (टी 1, टी 2) ((15.12.2.7) पर कब्जा रूपांतरण (.15.1.10) लागू करने का परिणाम है।

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

संभवत: जब कोई एक मान हो, तो उसका अपवाद होता है null
इरिक रॉबर्टसन

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

1
@ एरिक: मुझे नहीं लगता कि यह लागू booleanनहीं है क्योंकि यह एक संदर्भ प्रकार नहीं है।
13

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

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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.