2012-04-30 23 views
25

कल्पना कीजिए कि मेरे पास एक वर्ग परिवार है। इसमें व्यक्ति की एक सूची है। प्रत्येक (वर्ग) व्यक्ति में एक (वर्ग) पता होता है। प्रत्येक (कक्षा) पते में एक (कक्षा) पोस्टलकोड होता है। कोई भी "इंटरमीडिएट" कक्षा शून्य हो सकती है।जावा: नेस्टेड कक्षाओं (डीप नल जांच) में शून्य के लिए जांच से बचें

तो, क्या प्रत्येक चरण में शून्य के लिए चेककोड किए बिना पोस्टलकोड पहुंचने का एक आसान तरीका है? यानी, निम्नलिखित डेज़ी चेनिंग कोड से बचने का कोई तरीका है? मुझे पता है कि "मूल" जावा समाधान नहीं है, लेकिन उम्मीद है कि अगर कोई पुस्तकालय या कुछ जानता है। (चेक कॉमन्स & अमरूद और कुछ नहीं देखा)

if(family != null) { 
    if(family.getPeople() != null) { 
     if(family.people.get(0) != null) { 
      if(people.get(0).getAddress() != null) { 
       if(people.get(0).getAddress().getPostalCode() != null) { 
        //FINALLY MADE IT TO DO SOMETHING!!! 
       } 
      } 
     } 
    } 
} 

नहीं है, संरचना बदल नहीं सकते। यह एक ऐसी सेवा से है जिस पर मेरा नियंत्रण नहीं है।

नहीं, मैं ग्रोवी का उपयोग नहीं कर सकता और यह आसान "एल्विस" ऑपरेटर का उपयोग नहीं कर सकता।

नहीं, मैं जावा 8 के लिए प्रतीक्षा नहीं करना चाहते हैं: डी

मैं विश्वास नहीं कर सकता मैं पहली बार देव हूँ कभी बीमार पाने के लिए 'इस तरह लेखन कोड के एन थक, लेकिन मैं हेवन' एक समाधान खोजने में सक्षम नहीं है।

विचार?

धन्यवाद

-
llappall

+0

क्षमा करें, आप अटक गए हैं। कुछ लोग इसे थोड़ा कम घने बनाने के लिए ट्राइनरी सशर्त ऑपरेटर का उपयोग करते हैं, लेकिन यह अभी भी वही बाइटकोड है, पढ़ने के लिए मुश्किल है। –

+4

"* मुझे विश्वास नहीं है कि मैं बीमार होने वाला पहला देव हूं, इस तरह के कोड लिखने से थक गया हूं *" ठीक है, आप नहीं हैं। – user1329572

+0

निश्चित रूप से। लेकिन मुझे विश्वास नहीं है कि आप कोड को और अधिक हरा सकते हैं! माफ़ कीजिये! –

उत्तर

1

नहीं इस तरह के एक शांत विचार है, लेकिन अपवाद को पकड़ने के बारे में कैसे:

try 
    { 
     PostalCode pc = people.get(0).getAddress().getPostalCode(); 
    } 
    catch(NullPointerException ex) 
    { 
     System.out.println("Gotcha"); 
    } 
6

निकटतम आप प्राप्त कर सकते हैं कम का लाभ लेने के लिए है सशर्त में कटौती नियम:

if(family != null && family.getPeople() != null && family.people.get(0) != null && family.people.get(0).getAddress() != null && family.people.get(0).getAddress().getPostalCode() != null) { 
        //FINALLY MADE IT TO DO SOMETHING!!! 

} 

वैसे, अग्रिम स्थिति का परीक्षण करने के बजाय अपवाद को पकड़ना एक भयानक विचार है।

+0

आपके पास कुछ पहुंच प्राप्त है '}' (रिफैक्टर होने पर उन्हें हटाने के लिए भूल गए) – amit

+0

धन्यवाद, @amit। अब तय –

11

आपका कोड बर्ताव करता है एक ही

रूप
if(family != null && 
    family.getPeople() != null && 
    family.people.get(0) != null && 
    family.people.get(0).getAddress() != null && 
    family.people.get(0).getAddress().getPostalCode() != null) { 
     //My Code 
} 

short circuiting evaluation के लिए धन्यवाद, यह भी सुरक्षित है, के बाद से दूसरी शर्त का मूल्यांकन किया जा नहीं होगा अगर पहले गलत है, 3 यदि मूल्यांकन नहीं किया जाएगा दूसरा झूठा है, .... और आपको एनपीई नहीं मिलेगा क्योंकि अगर यह है।

+0

'शून्य' हमेशा हमारे कोड में रहना चाहते हैं! –

3

यदि यह दुर्लभ है तो आप null चेक को अनदेखा कर सकते हैं और NullPointerException पर भरोसा कर सकते हैं। संभावित प्रदर्शन समस्या के कारण "दुर्लभ" (निर्भर करता है, आमतौर पर स्टैक ट्रेस भर जाएगा जो महंगा हो सकता है)।

checkNonNull(family, "people[0].address.postalcode") 

कार्यान्वयन एक व्यायाम के रूप में छोड़ दिया:

कि 1) एक विशेष सहायक विधि है कि कि कोड को साफ करने या 2) प्रतिबिंब और इस तरह की स्ट्रिंग का उपयोग कर सामान्य दृष्टिकोण बनाने अशक्त के लिए जाँच करता अलावा

+0

प्रतिबिंब बिल्कुल सस्ता नहीं है। –

+0

हां वास्तव में यह धीमा हो सकता है @paul। विशेष रूप से विधि लुकअप आदि। वास्तविक विधि आविष्कार हालांकि काफी तेज हो सकता है, लेकिन फिर यह निर्भर करता है (अन्य अनुकूलन वीएम के लिए कठिन हो सकता है)। इसलिए यदि आवश्यक हो तो विधि/फ़ील्ड लुकअप को कैश करना मेरे अनुभव से आम तौर पर महत्वपूर्ण है। निश्चित रूप से यह सब इस बात पर निर्भर करता है कि कोड कितनी बार उपयोग किया जाता है। –

1

शून्य का उपयोग करने के बजाय, आप "शून्य ऑब्जेक्ट" डिज़ाइन पैटर्न के कुछ संस्करण का उपयोग कर सकते हैं।उदाहरण के लिए:

public class Family { 
    private final PersonList people; 
    public Family(PersonList people) { 
     this.people = people; 
    } 

    public PersonList getPeople() { 
     if (people == null) { 
      return PersonList.NULL; 
     } 
     return people; 
    } 

    public boolean isNull() { 
     return false; 
    } 

    public static Family NULL = new Family(PersonList.NULL) { 
     @Override 
     public boolean isNull() { 
      return true; 
     } 
    }; 
} 


import java.util.ArrayList; 

public class PersonList extends ArrayList<Person> { 
    @Override 
    public Person get(int index) { 
     Person person = null; 
     try { 
      person = super.get(index); 
     } catch (ArrayIndexOutOfBoundsException e) { 
      return Person.NULL; 
     } 
     if (person == null) { 
      return Person.NULL; 
     } else { 
      return person; 
     } 
    } 
    //... more List methods go here ... 

    public boolean isNull() { 
     return false; 
    } 

    public static PersonList NULL = new PersonList() { 
     @Override 
     public boolean isNull() { 
      return true; 
     } 
    }; 
} 

public class Person { 
    private Address address; 

    public Person(Address address) { 
     this.address = address; 
    } 

    public Address getAddress() { 
     if (address == null) { 
      return Address.NULL; 
     } 
     return address; 
    } 
    public boolean isNull() { 
     return false; 
    } 

    public static Person NULL = new Person(Address.NULL) { 
     @Override 
     public boolean isNull() { 
      return true; 
     } 
    }; 
} 

etc etc etc 

फिर अपने बयान हो सकता है अगर:

if (!family.getPeople().get(0).getAddress().getPostalCode.isNull()) {...} 

यह करने से इनकी है के बाद से:

  • आप हर वर्ग के लिए शून्य वस्तुओं बनाने अटक कर रहे हैं,
  • यह मुश्किल है इन वस्तुओं को सामान्य बनाने के लिए, इसलिए आप प्रत्येक सूची, मानचित्र, आदि का एक नल-ऑब्जेक्ट संस्करण बना रहे हैं, जिसका उपयोग आप करना चाहते हैं, और
  • उपclassing के साथ संभावित रूप से कुछ मजेदार मुद्दे हैं और जो उपयोग करने के लिए नल।

लेकिन यदि आप वास्तव में अपने == null से नफरत करते हैं, तो यह एक तरीका है।

0

मैं सिर्फ वही चीज़ ढूंढ रहा था (मेरा संदर्भ: स्वचालित रूप से जेएक्सबी कक्षाओं का एक समूह, और किसी भी तरह से मेरे पास .getFoo().getBar()... की लंबी डेज़ी-चेन है। अनिवार्य रूप से, मध्य में वापसी में से एक कॉल में शून्य, एनपीई का कारण बनता है।

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

/** 
* Using {@link java.lang.reflect.Method}, apply the given methods (in daisy-chain fashion) 
* to the array of Objects x. 
* 
* <p>For example, imagine that you'd like to express: 
* 
* <pre><code> 
* Fubar[] out = new Fubar[x.length]; 
* for (int i=0; {@code i<x.length}; i++) { 
* out[i] = x[i].getFoo().getBar().getFubar(); 
* } 
* </code></pre> 
* 
* Unfortunately, the correct code that checks for nulls at every level of the 
* daisy-chain becomes a bit convoluted. 
* 
* <p>So instead, this method does it all (checks included) in one call: 
* <pre><code> 
* Fubar[] out = apply(new Fubar[0], x, "getFoo", "getBar", "getFubar"); 
* </code></pre> 
* 
* <p>The cost, of course, is that it uses Reflection, which is slower than 
* direct calls to the methods. 
* @param type the type of the expected result 
* @param x the array of Objects 
* @param methods the methods to apply 
* @return 
*/ 
@SuppressWarnings("unchecked") 
public static <T> T[] apply(T[] type, Object[] x, String...methods) { 
    int n = x.length; 
    try { 
     for (String methodName : methods) { 
      Object[] out = new Object[n]; 
      for (int i=0; i<n; i++) { 
       Object o = x[i]; 
       if (o != null) { 
        Method method = o.getClass().getMethod(methodName); 
        Object sub = method.invoke(o); 
        out[i] = sub; 
       } 
      } 
      x = out; 
     } 
    T[] result = (T[])Array.newInstance(type.getClass().getComponentType(), n); 
    for (int i=0; i<n; i++) { 
      result[i] = (T)x[i]; 
    } 
      return result; 
    } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 
      throw new RuntimeException(e); 
    } 
} 
0

यदि, यदि आप जावा 8 का उपयोग कर रहे हैं तो आप इसका उपयोग कर सकते हैं;

resolve(() -> people.get(0).getAddress().getPostalCode()); 
    .ifPresent(System.out::println); 

: 
public static <T> Optional<T> resolve(Supplier<T> resolver) { 
    try { 
     T result = resolver.get(); 
     return Optional.ofNullable(result); 
    } 
    catch (NullPointerException e) { 
     return Optional.empty(); 
    } 
} 

REF: avoid null checks

+0

यह समाधान एनपीई को पकड़ने पर निर्भर करता है जो बहुत खराब प्रदर्शन करेगा। – mojoken

0

हालांकि इस पद लगभग पांच साल पुराना है, मैं कैसे NullPointerException रों को संभालने के लिए वर्ष की आयु वर्ष सवाल का एक और समाधान हो सकता है।

संक्षेप में:

end: { 
    List<People> people = family.getPeople();   if(people == null || people.isEmpty()) break end; 
    People person = people.get(0);      if(person == null) break end; 
    Address address = person.getAddress();    if(address == null) break end; 
    PostalCode postalCode = address.getPostalCode();  if(postalCode == null) break end; 

    System.out.println("Do stuff"); 
} 

के बाद से वहाँ भी उपयोग में विरासत कोड का एक बहुत, जावा 8 और Optional उपयोग कर रहा है एक विकल्प है हमेशा नहीं।

जब भी गहरा शामिल नेस्टेड वर्ग हैं (JAXB, साबुन, JSON, आप इसे नाम ...) और Law of Demeter लागू नहीं किया है, तो आप मूल रूप से सब कुछ की जाँच करें और अगर वहाँ संभव आसपास गुप्त NPEs हैं देखने के लिए है।

मेरा प्रस्तावित समाधान पठनीयता के लिए प्रयास करता है और यदि कम से कम 3 या अधिक घोंसला वाले वर्ग शामिल नहीं होते हैं (जब मैं नेस्टेड कहता हूं, तो मेरा मतलब औपचारिक संदर्भ में Nested classes नहीं है)। चूंकि कोड लिखे जाने से कहीं अधिक पढ़ा जाता है, इसलिए कोड के बाएं हिस्से में एक त्वरित नज़र से इसका अर्थ गहराई से घोंसला का उपयोग करने से अधिक स्पष्ट होगा।

आप और भाग की जरूरत है, तो आप इस पैटर्न का उपयोग कर सकते हैं:

boolean prematureEnd = true; 

end: { 
    List<People> people = family.getPeople();   if(people == null || people.isEmpty()) break end; 
    People person = people.get(0);      if(person == null) break end; 
    Address address = person.getAddress();    if(address == null) break end; 
    PostalCode postalCode = address.getPostalCode();  if(postalCode == null) break end; 

    System.out.println("Do stuff"); 
    prematureEnd = false; 
} 

if(prematureEnd) { 
    System.out.println("The else part"); 
} 

कुछ IDEs, इस स्वरूपण टूट जाएगा जब तक आप उन्हें हिदायत नहीं (this question देखें)।

आपकी सशर्तियों को उलटा होना चाहिए - जब आप इसे जारी रखना चाहिए, तो कोड को तब बताएं जब इसे जारी रखना चाहिए।

एक और बात - आपका कोड अभी भी टूटने का प्रवण है। आपको अपने कोड में पहली पंक्ति के रूप में if(family.getPeople() != null && !family.getPeople().isEmpty()) का उपयोग करना होगा, अन्यथा एक खाली सूची एनपीई फेंक देगी।

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

  • कोई संबंधित समस्या नहीं^_^