स्ट्रिंग स्विच स्टेटमेंट एक अशक्त मामले का समर्थन क्यों नहीं करता है?


125

मैं सिर्फ यह सोच रहा हूं कि जावा 7 switchस्टेटमेंट एक nullमामले का समर्थन क्यों नहीं करता और इसके बजाय फेंकता है NullPointerException? नीचे टिप्पणी लाइन देखें (उदाहरण जावा ट्यूटोरियल लेखswitch से लिया गया ):

{
    String month = null;
    switch (month) {
        case "january":
            monthNumber = 1;
            break;
        case "february":
            monthNumber = 2;
            break;
        case "march":
            monthNumber = 3;
            break;
        //case null:
        default: 
            monthNumber = 0;
            break;
    }

    return monthNumber;
}

यह ifहर switchउपयोग से पहले अशक्त जांच के लिए एक शर्त से बचना होगा ।


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

2
स्विच करने का प्रयास nullअपवाद का कारण होगा। के लिए एक ifचेक प्रदर्शन करें null, फिर switchबयान में जाएं।
गपरानी

28
से JLS : जावा प्रोग्रामिंग भाषा के डिजाइनरों के फैसले में, [एक फेंक NullPointerExceptionअगर करने के लिए अभिव्यक्ति का मूल्यांकन nullरनटाइम पर] चुपचाप पूरे स्विच बयान लंघन या (यदि हो तो) के बाद स्टेटमेंट्स को निष्पादित करने को चुनने की तुलना में बेहतर परिणाम है डिफ़ॉल्ट लेबल (यदि कोई हो)।
गपरानी

3
@gparyani: इसका जवाब दें। यह बहुत आधिकारिक और निश्चित लगता है।
थिलो

7
@JeffGohlke: "जब तक आप निर्णय लेने वाले व्यक्ति नहीं हैं, तब तक किसी प्रश्न का उत्तर देने का कोई तरीका नहीं है।" ... ठीक है, gparyani की टिप्पणी अन्यथा साबित होती है
user541686

जवाबों:


145

जैसा कि Damryfbfnetsi टिप्पणियों में बताते हैं , JLS has14.11 में निम्नलिखित नोट हैं:

nullस्विच लेबल के रूप में उपयोग करने के खिलाफ निषेध एक कोड को लिखने से रोकता है जिसे कभी भी निष्पादित नहीं किया जा सकता है। यदि switchअभिव्यक्ति एक संदर्भ प्रकार की है, Stringया एक बॉक्सिंग आदिम प्रकार या एक एनुम प्रकार है, तो एक रन-टाइम त्रुटि तब होगी जब अभिव्यक्ति nullरन टाइम पर मूल्यांकन करती है । जावा प्रोग्रामिंग भाषा के डिजाइनरों के फैसले में, यह पूरे switchबयान को चुपचाप छोड़ देने या defaultलेबल (यदि कोई हो) के बाद कथनों (यदि कोई हो) को निष्पादित करने के लिए चुनने से बेहतर परिणाम है ।

(जोर मेरा)

जबकि अंतिम वाक्य का उपयोग करने की संभावना से अधिक है case null:, यह उचित लगता है और भाषा डिजाइनरों के इरादों में एक दृश्य प्रस्तुत करता है।

यदि हम कार्यान्वयन विवरणों को देखते हैं, तो क्रिश्चियन हुजेर के इस ब्लॉग पोस्टnull में स्विच में अनुमति नहीं देने के बारे में कुछ व्यावहारिक अटकलें हैं (हालांकि यह enumस्विच के बजाय स्विच पर केंद्रित है String):

हुड के तहत, switchबयान आम तौर पर एक tablesswitch बाइट कोड के लिए संकलन होगा। और "भौतिक" तर्क के switchसाथ-साथ इसके मामले भी हैं int। स्विच करने का इंटिमेट वैल्यू विधि को लागू करके निर्धारित किया जाता है Enum.ordinal()। [...] अध्यादेश शून्य से शुरू होते हैं।

इसका मतलब है कि मानचित्रण nullके लिए 0एक अच्छा विचार नहीं होगा। पहले ईनम मान पर एक स्विच अशक्त से अप्रभेद्य होगा। हो सकता है कि यह एक अच्छा विचार रहा होगा कि 1. अध्यादेशों के लिए अध्यादेशों को गिनना शुरू किया जाए। हालांकि, इसे इस तरह परिभाषित नहीं किया गया है और इस परिभाषा को बदला नहीं जा सकता है।

जबकि Stringस्विच अलग तरीके से कार्यान्वित किए जाते हैं , enumस्विच पहले आया और संदर्भ के प्रकार को स्विच करने के लिए मिसाल दी गई है कि जब संदर्भ होता है तो व्यवहार करना चाहिए null


1
case null:यदि यह इसके लिए विशेष रूप से कार्यान्वित किया गया होता, तो इसके संचालन के रूप में अशक्त संचालन की अनुमति देने के लिए यह एक बड़ा सुधार होगा String। वर्तमान में सभी Stringजाँच के लिए किसी भी तरह से शून्य-जाँच की आवश्यकता होती है, यदि हम इसे सही करना चाहते हैं, हालाँकि ज्यादातर अंतर्निहित रूप से स्ट्रिंग को पहले लगाकर "123test".equals(value)। अब हम अपने स्विच स्टेटमेंट को लिखने के लिए मजबूर हैंif (value != null) switch (value) {...
YoYo

1
पुन: "0 से शून्य मैपिंग एक अच्छा विचार नहीं होगा": यह एक समझदारी है क्योंकि ".hashcode () का मान 0 है!" इसका मतलब यह होगा कि एक शून्य स्ट्रिंग और एक शून्य लंबाई स्ट्रिंग को स्विच स्टेटमेंट में पहचान के साथ व्यवहार करना होगा, जो स्पष्ट रूप से व्यवहार्य नहीं है।
स्कोमीसा

एनम के मामले के लिए, उन्हें शून्य से -1 मैप करने से क्या रोक रहा था?
क्रिस्पी १py

31

सामान्य रूप nullसे संभाल करने के लिए बुरा है; शायद एक बेहतर भाषा बिना रह सकती है null

आपकी समस्या हल हो सकती है

    switch(month==null?"":month)
    {
        ...
        //case "":
        default: 
            monthNumber = 0;

    }

एक अच्छा विचार नहीं है अगर monthएक खाली स्ट्रिंग है: यह इसे एक अशक्त स्ट्रिंग के समान व्यवहार करेगा।
गपरानी

13
रिक्त स्ट्रिंग के रूप में नल का इलाज करने वाले कई मामलों के लिए पूरी तरह से उचित हो सकता है
एरिक वुड्रूफ़

23

यह सुंदर नहीं है, लेकिन String.valueOf()आपको एक स्विच में अशक्त स्ट्रिंग का उपयोग करने की अनुमति देता है। यदि यह पाया जाता है null, तो यह इसे धर्मान्तरित करता है "null", अन्यथा यह केवल उसी स्ट्रिंग को वापस करता है जिसे आपने इसे पारित किया था। यदि आप "null"स्पष्ट रूप से नहीं संभालते हैं , तो यह जाएगा default। एकमात्र चेतावनी यह है कि स्ट्रिंग "null"और वास्तविक nullचर के बीच अंतर करने का कोई तरीका नहीं है ।

    String month = null;
    switch (String.valueOf(month)) {
        case "january":
            monthNumber = 1;
            break;
        case "february":
            monthNumber = 2;
            break;
        case "march":
            monthNumber = 3;
            break;
        case "null":
            monthNumber = -1;
            break;
        default: 
            monthNumber = 0;
            break;
    }
    return monthNumber;

2
मेरा मानना ​​है कि जावा में ऐसा कुछ करना एक एंटीपैटर्न है।
30ukasz Rzeszotarski

2
@ That'sukaszRzeszotarski "मैं बहुत सुंदर नहीं हूँ" से बहुत ज्यादा मतलब है
krispy

15

यह जवाब देने की कोशिश है कि यह क्यों फेंकता है NullPointerException

नीचे दिए गए javap कमांड के आउटपुट से पता चलता है कि तर्क स्ट्रिंग के हैशकोड के caseआधार पर चुना गया switchहै और इसलिए .hashCode()null स्ट्रिंग पर लागू होने पर NPE को फेंकता है।

6: invokevirtual #18                 // Method java/lang/String.hashCode:()I
9: lookupswitch  { // 3
    -1826660246: 44
     -263893086: 56
      103666243: 68
        default: 95
   }

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

    int monthNumber;
    String month = args[0];

    switch (month) {
    case "Ea":
        monthNumber = 1;
        break;
    case "FB":
        monthNumber = 2;
        break;
    // case null:
    default:
        monthNumber = 0;
        break;
    }
    System.out.println(monthNumber);

जिसके लिए javap

  10: lookupswitch  { // 1
              2236: 28
           default: 59
      }
  28: aload_3       
  29: ldc           #22                 // String Ea
  31: invokevirtual #24                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
  34: ifne          49
  37: aload_3       
  38: ldc           #28                 // String FB
  40: invokevirtual #24                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
  43: ifne          54
  46: goto          59 //Default

आप देख सकते हैं केवल एक ही मामला उत्पन्न हो जाता है के लिए "Ea"और "FB"लेकिन दोनों के साथ ifकी स्थिति प्रत्येक मामले तार के साथ एक मैच के लिए जाँच करने के लिए। इस कार्यक्षमता को लागू करने का बहुत दिलचस्प और जटिल तरीका!


5
हालांकि इसे दूसरे तरीके से भी लागू किया जा सकता था।
थिलो

2
मैं कहूंगा कि यह एक डिज़ाइन बग है।
हिरण हंटर

6
मुझे आश्चर्य है कि अगर स्ट्रिंग हैश फ़ंक्शन के कुछ संदिग्ध पहलुओं में बदलाव नहीं हुआ है: सामान्य कोड hashCodeमें किसी प्रोग्राम के अलग-अलग रनों पर समान मूल्यों को वापस करने पर भरोसा नहीं करना चाहिए , लेकिन चूंकि स्ट्रिंग हैश द्वारा निष्पादित किया जाता है संकलक, स्ट्रिंग हैश विधि भाषा युक्ति का हिस्सा बनकर समाप्त होती है।
सुपरकैट

5

लंबी कहानी छोटी ... (और काफी दिलचस्प उम्मीद है !!!)

Enum पहले में पेश किए गए Java1.5 ( Sep'2004 ) और बग करने का अनुरोध स्ट्रिंग पर स्विच की अनुमति देने के बहुत पहले (दायर किया गया था Oct'95 )। यदि आप Jun'2004 पर उस बग पर पोस्ट की गई टिप्पणी को देखते हैं , तो Don't hold your breath. Nothing resembling this is in our plans.ऐसा लगता है कि वे इस बग को स्थगित ( नजरअंदाज ) कर रहे हैं और अंततः उसी वर्ष जावा 1.5 लॉन्च किया, जिसमें उन्होंने 0 से शुरू होने वाले अध्यादेश के साथ 'एनम' पेश किया और फैसला किया ( याद किया ) एनम के लिए अशक्त का समर्थन नहीं करने के लिए। बाद में Java1.7 ( Jul'2011 ) में उन्होंने ( मजबूर किया)) स्ट्रिंग के साथ एक ही दर्शन (जैसे कि बाइटकोड को जनरेट करते समय हैशकोड () विधि) को कॉल करने से पहले कोई शून्य जांच नहीं की गई थी।

इसलिए मुझे लगता है कि यह इस तथ्य को उबालता है कि एनम पहले आया था और 0 से शुरू होने के साथ इसे लागू किया गया था, जिसके कारण वे स्विच ब्लॉक में शून्य मान का समर्थन नहीं कर सके और बाद में स्ट्रिंग के साथ उन्होंने उसी दर्शन को मजबूर करने का फैसला किया यानी शून्य मान नहीं स्विच ब्लॉक में अनुमति है।

TL, DR स्ट्रिंग के साथ वे NPE (null के लिए हैशकोड बनाने के प्रयास के कारण) का ख्याल रख सकते थे, जबकि java कोड को byte कोड रूपांतरण में लागू करते थे, लेकिन अंत में फैसला नहीं किया।

Ref: TheBUG , JavaVersionHistory , JavaCodeToByteCode , SO


1

जावा डॉक्स के अनुसार:

एक स्विच बाइट के साथ काम करता है, लघु, चार और आंतरिक आदिम डेटा प्रकार। यह एन्यूमरेटेड टाइप्स (एनम टाइप्स में चर्चा की गई), स्ट्रिंग क्लास, और कुछ विशेष वर्गों के साथ काम करता है जो कुछ आदिम प्रकारों को लपेटते हैं: कैरेक्टर, बाइट, शॉर्ट और इंटेगर (नंबरों और स्ट्रिंग्स में चर्चा की गई)।

चूंकि nullकोई प्रकार नहीं है, और किसी भी चीज का उदाहरण नहीं है, यह स्विच स्टेटमेंट के साथ काम नहीं करेगा।


4
और फिर भी nullएक के लिए मान्य मान है String, Character, Byte, Short, या Integerसंदर्भ।
एस्टरी

0

इसका उत्तर बस इतना है कि यदि आप एक संदर्भ प्रकार (जैसे एक बॉक्सिंग आदिम प्रकार) के साथ स्विच का उपयोग करते हैं, तो रन-टाइम त्रुटि तब होगी जब अभिव्यक्ति शून्य है क्योंकि इसे अनबॉक्स करना एनपीई को फेंक देगा।

इसलिए मामला अशक्त (जो अवैध है) कभी भी निष्पादित नहीं किया जा सकता है;)


1
हालांकि इसे दूसरे तरीके से भी लागू किया जा सकता था।
थिलो

ठीक है @ थिलो, मेरे से अधिक चालाक लोग इस कार्यान्वयन में शामिल थे। यदि आप ऐसे अन्य तरीकों के बारे में जानते हैं, जिन पर इसे लागू किया जा सकता है, तो मुझे यह जानना अच्छा लगेगा कि वे क्या हैं [और मुझे यकीन है कि अन्य हैं] इसलिए शेयर करें ...
amrith

3
स्ट्रिंग्स बॉक्सिंग आदिम प्रकार नहीं हैं, और एनपीई इसलिए नहीं होता है क्योंकि कोई उन्हें "अनबॉक्स" करने की कोशिश कर रहा है।
थिलो

@thilo, इसे लागू करने के अन्य तरीके हैं, क्या?
amrith

3
if (x == null) { // the case: null part }
थिलो

0

मैं @Paul Bellora के उत्तर में https://stackoverflow.com/a/18263594/1053496 पर हर्षजनक टिप्पणियों से सहमत हूं ।

मैंने अपने अनुभव से एक और कारण पाया।

यदि 'केस' शून्य हो सकता है अर्थात स्विच (वेरिएबल) शून्य है, तो जब तक डेवलपर मैचिंग 'नल' केस प्रदान करता है तब हम तर्क दे सकते हैं कि यह ठीक है। लेकिन क्या होगा यदि डेवलपर किसी भी मिलान 'शून्य' मामले को प्रदान नहीं करता है। फिर हमें इसे एक 'डिफ़ॉल्ट' केस से मिलाना होगा, जो कि डेवलपर ने डिफॉल्ट केस में हैंडल करने का इरादा नहीं किया है। इसलिए 'null' को डिफ़ॉल्ट रूप से मिलान करना 'आश्चर्यजनक व्यवहार' का कारण बन सकता है। इसलिए 'एनपीई' को फेंकने से डेवलपर को हर मामले को स्पष्ट रूप से निपटाना होगा। मैंने इस मामले में NPE को बहुत सोच समझकर फेंक दिया।


0

Apache StringUtils वर्ग का उपयोग करें

String month = null;
switch (StringUtils.trimToEmpty(month)) {
    case "xyz":
        monthNumber=1;  
    break;
    default:
       monthNumber=0;
    break;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.