IllegalArgumentException को कब फेंकना चाहिए?


98

मुझे चिंता है कि यह एक रनटाइम अपवाद है इसलिए इसे शायद ही इस्तेमाल किया जाना चाहिए।
मानक उपयोग मामला:

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

लेकिन ऐसा लगता है कि यह निम्नलिखित डिजाइन को मजबूर करेगा:

public void computeScore() throws MyPackageException {
      try {
          setPercentage(userInputPercent);
      }
      catch(IllegalArgumentException exc){
           throw new MyPackageException(exc);
      }
 }

इसे जाँच अपवाद के रूप में वापस पाने के लिए।

ठीक है, लेकिन चलो उसके साथ चलो। यदि आप खराब इनपुट देते हैं, तो आपको रनटाइम त्रुटि मिलती है। तो सबसे पहले यह वास्तव में समान रूप से लागू करने के लिए एक काफी कठिन नीति है, क्योंकि आपको बहुत विपरीत रूपांतरण करना पड़ सकता है:

public void scanEmail(String emailStr, InputStream mime) {
    try {
        EmailAddress parsedAddress = EmailUtil.parse(emailStr);
    }
    catch(ParseException exc){
        throw new IllegalArgumentException("bad email", exc);
    }
}

और इससे भी बदतर - 0 <= pct && pct <= 100क्लाइंट कोड की जाँच करते समय , सांख्यिकीय रूप से करने की अपेक्षा की जा सकती है, यह अधिक उन्नत डेटा के लिए ऐसा नहीं है जैसे कि ईमेल पता, या इससे भी बदतर, ऐसा कुछ जिसे डेटाबेस के खिलाफ जांचना पड़ता है, इसलिए सामान्य ग्राहक कोड में पूर्व नहीं हो सकता है- सत्यापित करें।

इसलिए मूल रूप से मैं जो कह रहा हूं वह यह है कि मैं इसके उपयोग के लिए एक सार्थक सुसंगत नीति नहीं देखता IllegalArgumentException। ऐसा लगता है कि इसका उपयोग नहीं किया जाना चाहिए और हमें अपने स्वयं के चेक किए गए अपवादों से चिपके रहना चाहिए। इसे फेंकने के लिए एक अच्छा उपयोग मामला क्या है?

जवाबों:


80

एपीआई डॉक्टर के लिए IllegalArgumentException:

यह इंगित करने के लिए फेंक दें कि एक विधि एक अवैध या अनुचित तर्क पारित किया गया है।

में देखने से यह कैसे JDK पुस्तकालयों में प्रयोग किया जाता है , मैं कहूंगा कि:

  • यह स्पष्ट रूप से खराब इनपुट के बारे में शिकायत करने के लिए एक रक्षात्मक उपाय की तरह लगता है इससे पहले कि इनपुट कामों में लग जाए और एक निरर्थक त्रुटि संदेश के साथ आधे रास्ते में विफल हो जाए।

  • यह उन मामलों के लिए उपयोग किया जाता है, जहां चेक किए गए अपवाद को फेंकना बहुत कष्टप्रद होगा (हालांकि यह java.lang.reflect कोड में एक उपस्थिति बनाता है, जहां चेक-अपवाद-फेंकने के हास्यास्पद स्तरों के बारे में चिंता अन्यथा स्पष्ट नहीं है)।

मैं IllegalArgumentExceptionआम उपयोगिताओं (JDK उपयोग के साथ संगत रहने की कोशिश कर रहा) के लिए अंतिम खाई रक्षात्मक तर्क जाँच करने के लिए उपयोग करेगा। या जहां उम्मीद है कि एक खराब तर्क एक प्रोग्रामर त्रुटि है, एक के समान NullPointerException। मैं इसका उपयोग व्यवसाय कोड में सत्यापन को लागू करने के लिए नहीं करूंगा। मैं निश्चित रूप से ईमेल उदाहरण के लिए इसका इस्तेमाल नहीं करेगा।


8
मुझे लगता है कि सलाह "जहां उम्मीद है कि एक बुरा तर्क एक प्रोग्रामर त्रुटि है" सबसे अधिक संगत है कि मैंने इसे कैसे देखा है, इसलिए इस उत्तर को स्वीकार करें।
djechlin

22

"खराब इनपुट" के बारे में बात करते समय, आपको विचार करना चाहिए कि इनपुट कहां से आ रहा है।

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

यदि इनपुट आपके स्वयं के सिस्टम, जैसे आपके डेटाबेस, या आपके एप्लिकेशन के कुछ अन्य हिस्सों से उत्पन्न होता है, तो आपको उस पर भरोसा करने में सक्षम होना चाहिए (यह वहां पहुंचने से पहले इसे मान्य किया जाना चाहिए था)। इस मामले में एक IllegalArgumentException जैसे अनियंत्रित अपवाद को फेंकना पूरी तरह से ठीक है, जिसे पकड़ा नहीं जाना चाहिए (सामान्य तौर पर आपको कभी भी अनियंत्रित अपवाद नहीं पकड़ना चाहिए)। यह प्रोग्रामर की त्रुटि है कि अमान्य मान पहले स्थान पर मिला है;) आपको इसे ठीक करने की आवश्यकता है।


2
क्यों "आपको कभी भी अनियंत्रित अपवाद नहीं पकड़ना चाहिए"?
कोरे तुगाय

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

1
Because an unchecked exception is meant to be thrown as a result of a programming errorमेरे सिर में बहुत सारा सामान साफ ​​करने में मेरी मदद की, धन्यवाद :)
सरोगेट

14

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

यदि वसूली की कोई उम्मीद नहीं है, तो अनियंत्रित अपवादों का उपयोग करने के लिए स्वतंत्र महसूस करें; उन्हें पकड़ने का कोई मतलब नहीं है, इसलिए यह पूरी तरह से ठीक है।

हालांकि यह आपके उदाहरण से 100% स्पष्ट नहीं है कि यह उदाहरण आपके कोड में है।


मुझे लगता है कि "काफी हद तक ठीक होने की उम्मीद है" वीसली है। कोई भी ऑपरेशन foo(data)उस हिस्से के रूप for(Data data : list) foo(data);में हो सकता था जिसमें कॉल करने वाला चाहे जितना सफल हो सकता है, भले ही कुछ डेटा विकृत हों। प्रोग्रामेटिक त्रुटियां भी शामिल हैं, अगर मेरे आवेदन में विफल होने का मतलब है कि लेन-देन संभवत: बेहतर नहीं होगा, अगर इसका मतलब है कि परमाणु शीतलन ऑफ़लाइन हो जाता है जो खराब है।
djechlin

StackOverflowErrorऔर ऐसे मामले हैं कि कॉल करने वाले से ठीक-ठीक अपेक्षा नहीं की जा सकती है। लेकिन ऐसा लगता है कि किसी भी डेटा या एप्लिकेशन लॉजिक स्तर के मामले की जांच होनी चाहिए। इसका मतलब है कि अपने अशक्त सूचक जाँच करें!
djechlin

4
एक परमाणु शीतलन अनुप्रयोग में, मैं परीक्षण में कठिन असफल हो जाता हूं, जबकि एक प्रोग्रामर ने सोचा था कि किसी का ध्यान नहीं जाना चाहिए।
लुई वासरमैन

Boolean.parseBoolean (..), एक अवैध तरीके से फेंकता है भले ही "कॉल करने वाले से ठीक होने की उम्मीद की जा सकती है।" इसलिए ..... इसे संभालने के लिए आपके कोड तक या कॉल करने वाले को वापस करने में विफल।
Jeryl कुक

5

जैसा कि oracle आधिकारिक ट्यूटोरियल में निर्दिष्ट है, यह बताता है कि:

यदि किसी ग्राहक से किसी अपवाद से उबरने की उम्मीद की जा सकती है, तो उसे एक अपवाद बना दें। यदि कोई ग्राहक अपवाद से उबरने के लिए कुछ नहीं कर सकता है, तो इसे अनियंत्रित अपवाद बनाएं।

अगर मैं एक आवेदन डेटाबेस का उपयोग कर के साथ बातचीत की है JDBC, और मैं एक विधि के रूप में तर्क लेता है int itemऔर double pricepriceके लिए इसी मद डेटाबेस तालिका से पढ़ा जाता है। मैं केवल मूल्य के itemसाथ खरीदी गई कुल संख्या को गुणा करता हूं priceऔर परिणाम लौटाता हूं । हालाँकि मुझे हमेशा अपने अंत (एप्लिकेशन के अंत) पर यकीन है कि तालिका में मूल्य क्षेत्र का मूल्य कभी भी नकारात्मक नहीं हो सकता है। लेकिन क्या होगा यदि मूल्य मूल्य नकारात्मक निकलता है ? यह दिखाता है कि डेटाबेस की ओर से एक गंभीर मुद्दा है। शायद ऑपरेटर द्वारा गलत मूल्य प्रविष्टि। यह इस तरह की समस्या है कि उस पद्धति को कॉल करने वाले एप्लिकेशन का दूसरा भाग अनुमानित नहीं कर सकता है और इससे उबर नहीं सकता है। यह BUGआपके डेटाबेस में है। तो औरIllegalArguementException()इस मामले में फेंक दिया जाना चाहिए जो बताता है कि the price can't be negative
मुझे उम्मीद है कि मैंने अपनी बात स्पष्ट रूप से व्यक्त की है।


मुझे यह (ओरेकल की) सलाह पसंद नहीं है क्योंकि अपवाद से निपटने के बारे में है कि कैसे ठीक किया जाए, न कि पुनर्प्राप्त करने के लिए। उदाहरण के लिए एक विकृत उपयोगकर्ता अनुरोध पूरे वेब सर्वर को क्रैश करने के लायक नहीं है।
djechlin

5

किसी भी एपीआई को निष्पादित करने से पहले किसी भी सार्वजनिक विधि के हर पैरामीटर की वैधता की जांच करनी चाहिए:

void setPercentage(int pct, AnObject object) {
    if( pct < 0 || pct > 100) {
        throw new IllegalArgumentException("pct has an invalid value");
    }
    if (object == null) {
        throw new IllegalArgumentException("object is null");
    }
}

वे आवेदन में 99.9% बार त्रुटियों का प्रतिनिधित्व करते हैं क्योंकि यह असंभव संचालन के लिए पूछ रहा है इसलिए अंत में वे बग हैं जो एप्लिकेशन को क्रैश कर देना चाहिए (इसलिए यह एक गैर पुनर्प्राप्ति योग्य त्रुटि है)।

इस स्थिति में और तेजी से विफल होने के दृष्टिकोण का पालन करते हुए आपको एप्लिकेशन को एप्लिकेशन स्थिति को दूषित करने से बचने के लिए समाप्त करना चाहिए।


इसके विपरीत, यदि कोई API क्लाइंट मुझे खराब इनपुट देता है, तो मुझे अपना संपूर्ण API सर्वर क्रैश नहीं करना चाहिए ।
djechlin

2
बेशक, यह आपके एपीआई सर्वर को क्रैश नहीं करना चाहिए, लेकिन कॉल करने वाले के लिए एक अपवाद लौटाएं जो क्लाइंट को कुछ भी क्रैश नहीं करना चाहिए।
इग्नासियो सोलर गार्सिया

आपने टिप्पणी में जो लिखा है, वह वह नहीं है जो आपने उत्तर में लिखा है।
djechlin

1
मुझे समझाते हैं, अगर एपीआई को गलत मापदंडों (एक बग) के साथ कॉल थर्ड पार्टी क्लाइंट द्वारा किया जाता है, तो क्लाइंट को क्रैश होना चाहिए। यदि यह API सर्वर गलत बग के साथ विधि को कॉल करने वाले बग के साथ है तो API सर्वर क्रैश हो जाना चाहिए। जाँच करें: en.wikipedia.org/wiki/Fail-fast
इग्नासियो सोलर गार्सिया

2

इलाज IllegalArgumentExceptionएक के रूप में पूर्व शर्त की जाँच करें, और डिजाइन सिद्धांत पर विचार करें: एक सार्वजनिक विधि दोनों पता है और सार्वजनिक रूप से अपने स्वयं के पूर्व शर्त दस्तावेज़ चाहिए।

मैं मानूंगा कि यह उदाहरण सही है:

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

यदि EmailUtil अपारदर्शी है , तो इसका अर्थ यह है कि पूर्व-उपयोगकर्ता के लिए पूर्व शर्त का वर्णन नहीं किया जा सकता है, तो एक जाँच अपवाद सही है। दूसरा संस्करण, इस डिजाइन के लिए सही:

import com.someoneelse.EmailUtil;

public void scanEmail(String emailStr, InputStream mime) throws ParseException {
    EmailAddress parsedAddress = EmailUtil.parseAddress(emailStr);
}

यदि EmailUtil पारदर्शी है , तो उदाहरण के लिए, यह प्रश्न के अंतर्गत वर्ग के स्वामित्व वाली एक निजी विधि है, IllegalArgumentExceptionयह सही है कि क्या और केवल तभी, जब इसके दस्तावेज़ीकरण को फ़ंक्शन प्रलेखन में वर्णित किया जा सकता है। यह एक सही संस्करण है:

/** @param String email An email with an address in the form abc@xyz.com
 * with no nested comments, periods or other nonsense.
 */
public String scanEmail(String email)
  if (!addressIsProperlyFormatted(email)) {
      throw new IllegalArgumentException("invalid address");
  }
  return parseEmail(emailAddr);
}
private String parseEmail(String emailS) {
  // Assumes email is valid
  boolean parsesJustFine = true;
  // Parse logic
  if (!parsesJustFine) {
    // As a private method it is an internal error if address is improperly
    // formatted. This is an internal error to the class implementation.
    throw new AssertError("Internal error");
  }
}

यह डिजाइन किसी भी तरह से जा सकता है।

  • यदि पूर्व शर्त वर्णन करना महंगा है, या यदि कक्षा का उपयोग उन ग्राहकों द्वारा किया जाना है जो यह नहीं जानते कि उनके ईमेल वैध हैं, तो उपयोग करें ParseException। यहां शीर्ष स्तर की विधि का नाम दिया गया है, scanEmailजो संकेत देता है कि अंतिम उपयोगकर्ता अनस्टाइड ईमेल भेजने का इरादा रखता है, इसलिए यह सही होने की संभावना है।
  • यदि पूर्वनिर्धारण को फ़ंक्शन प्रलेखन में वर्णित किया जा सकता है, और वर्ग अमान्य इनपुट के लिए इरादा नहीं करता है और इसलिए प्रोग्रामर त्रुटि का संकेत दिया जाता है, उपयोग करें IllegalArgumentException। यद्यपि "चेक" नहीं किया गया है "चेक" फ़ंक्शन का दस्तावेजीकरण करने वाले जावदोक में जाता है, जिसे ग्राहक को पालन करने की उम्मीद है। IllegalArgumentExceptionजहां ग्राहक अपना तर्क नहीं बता सकता है कि यह पहले से गलत है।

IllegalStateException पर एक नोट : इसका मतलब है "यह ऑब्जेक्ट की आंतरिक स्थिति (निजी उदाहरण चर) इस क्रिया को करने में सक्षम नहीं है।" अंतिम उपयोगकर्ता निजी स्थिति को नहीं देख सकता है, इसलिए यह बोलते हुए कि यह IllegalArgumentExceptionउस मामले में पूर्वता लेता है जहां ग्राहक के पास वस्तु की स्थिति को जानने का कोई तरीका नहीं है। चेक किए गए अपवादों पर पसंद किए जाने पर मेरे पास एक अच्छी व्याख्या नहीं है, हालांकि दो बार आरम्भ करने या डेटाबेस कनेक्शन को खोने जैसी चीजें बरामद नहीं हुई हैं, उदाहरण हैं।

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