अधिक प्रभावी रूप से @ निबल और @ नोनल एनोटेशन का उपयोग कैसे करें?


140

मुझे लगता है कि देख सकते हैं @Nullableऔर @Nonnullएनोटेशन सकता है को रोकने में सहायक हो NullPointerExceptionरों लेकिन वे बहुत दूर का प्रचार नहीं करते।

  • इन एनोटेशन की प्रभावशीलता अप्रत्यक्ष के एक स्तर के बाद पूरी तरह से बंद हो जाती है, इसलिए यदि आप केवल कुछ जोड़ते हैं तो वे बहुत दूर तक प्रचार नहीं करते हैं।
  • चूंकि इन एनोटेशन को अच्छी तरह से लागू नहीं किया जाता है, इसलिए मान के साथ चिह्नित होने का एक खतरा @Nonnullहै जो शून्य नहीं है और इसके परिणामस्वरूप सुस्त जांच नहीं कर रहा है।

नीचे दिया गया कोड किसी भी शिकायत को उठाए बिना एक पैरामीटर को चिह्नित करता @Nonnullहै nullNullPointerExceptionजब यह चलाया जाता है तो यह फेंकता है।

public class Clazz {
    public static void main(String[] args){
        Clazz clazz = new Clazz();

        // this line raises a complaint with the IDE (IntelliJ 11)
        clazz.directPathToA(null);

        // this line does not
        clazz.indirectPathToA(null); 
    }

    public void indirectPathToA(Integer y){
        directPathToA(y);
    }

    public void directPathToA(@Nonnull Integer x){
        x.toString(); // do stuff to x        
    }
}

क्या इन टिप्पणियों को अधिक सख्ती से लागू करने और / या आगे प्रचार करने का कोई तरीका है?


1
मुझे इसका विचार पसंद है @Nullableया नहीं @Nonnull, लेकिन अगर वे इसके लायक हैं, तो "बहस की संभावना" बहुत है
मार्टेन बॉडेस 23

मुझे लगता है कि एक ऐसी दुनिया में जाने का तरीका जहां यह एक संकलक त्रुटि या चेतावनी का कारण बनता है, एक अशक्त चर के साथ एक विधि को @Nonnullबुलाते समय कलाकारों की आवश्यकता होती है @Nonnull। बेशक, जावा 7 में एक एनोटेशन के साथ कास्टिंग संभव नहीं है, लेकिन जावा 8 एक चर के उपयोग के लिए एनोटेशन को लागू करने की क्षमता को शामिल करेगा, जिसमें कलाकार भी शामिल हैं। इसलिए यह जावा 8. में लागू होने के लिए संभव हो सकता है
थिओडोर मर्डॉक

1
@ TheodoreMurdock, हाँ, Java 8 में एक कास्ट (@NonNull Integer) yका निर्माण संभव रूप से संभव है, लेकिन एक संकलक को एनोटेशन के आधार पर किसी विशिष्ट बाइट कोड का उत्सर्जन करने की अनुमति नहीं है। रनटाइम अभिकथन के लिए बग हेलिकॉप्टर विधियाँ पर्याप्त हैं जैसा कि Bugs.eclipse.org/442103 (जैसे, directPathToA(assertNonNull(y))) में चर्चा की गई है - लेकिन आपको लगता है, यह केवल तेजी से विफल होने में मदद करता है। एकमात्र सुरक्षित तरीका एक वास्तविक अशक्त जांच (प्लस शाखा में वैकल्पिक रूप से वैकल्पिक कार्यान्वयन) है।
Stephan Herrmann

1
यह कहना इस प्रश्न में सहायक होगा कि आप @Nonnullऔर @Nullableकिसके बारे में बात कर रहे हैं, जैसे कि कई समान घोषणाएँ हैं ( यह प्रश्न देखें )। क्या आप पैकेज में एनोटेशन के बारे में बात कर रहे हैं javax.annotation?
जेम्स डन

1
@TJamesBoone इस सवाल के संदर्भ के लिए यह कोई फर्क नहीं पड़ता, यह प्रभावी ढंग से उनमें से किसी का उपयोग करने के बारे में था।
माइक रायलैंडर

जवाबों:


66

संक्षिप्त उत्तर: मुझे लगता है कि ये एनोटेशन आपके आईडीई के लिए केवल संभावित अशक्त सूचक त्रुटियों से सावधान करने के लिए उपयोगी हैं।

जैसा कि "क्लीन कोड" पुस्तक में कहा गया है, आपको अपनी सार्वजनिक पद्धति के मापदंडों की जांच करनी चाहिए और साथ ही साथ आक्रमणकारियों की जाँच करने से भी बचना चाहिए।

एक और अच्छा टिप शून्य मान कभी नहीं लौट रहा है, लेकिन इसके बजाय नल ऑब्जेक्ट पैटर्न का उपयोग कर रहा है।


10
वापसी मूल्यों के लिए संभवतः खाली होने पर, मैं दृढ़ता Optionalसे सादे के बजाय प्रकार का उपयोग करने का सुझाव देता हूंnull
पैट्रिक

7
वैकल्पिक आईएसटी "नल" की तुलना में बेहतर नहीं है। वैकल्पिक # मिलता है () NoSuchElementException को फेंकता है जबकि नल का एक उपयोग NullPointerException फेंकता है। दोनों सार्थक विवरण के बिना RuntimeException हैं। मैं अशक्त चर पसंद करते हैं।
30th

4
@ 30th आप Optional.get का उपयोग क्यों करेंगे () सीधे और नहीं Optional.isPresent () या Optional.map पहले?
गौरव

7
@ गौरव आप एक अशक्त चर का उपयोग सीधे क्यों करेंगे और जाँच नहीं करेंगे, यदि यह पहले शून्य है? ;-)
30 वीं

5
Optionalइस मामले में अंतर और अशक्त यह है कि Optionalबेहतर संचार करता है कि यह मान जानबूझकर खाली हो सकता है। निश्चित रूप से, यह एक जादू की छड़ी नहीं है और रनटाइम में यह ठीक उसी तरह से विफल हो सकती है जैसे कि अशक्त चर। हालांकि, प्रोग्रामर द्वारा एपीआई रिसेप्शन Optionalमेरी राय में बेहतर है ।
user1053510

31

आपके आईडीई के अलावा आपको संकेत देते समय आप ऐसे nullतरीकों से गुजरते हैं जो इस तर्क की उम्मीद करते हैं कि अशक्त नहीं हैं, इसके और भी फायदे हैं:

  • स्टेटिक कोड विश्लेषण उपकरण आपके IDE (जैसे FindBugs) के समान परीक्षण कर सकते हैं
  • इन अभिकथनों की जांच के लिए आप AOP का उपयोग कर सकते हैं

यह आपके कोड को अधिक बनाए रखने में मदद कर सकता है (क्योंकि आपको nullचेक की आवश्यकता नहीं है ) और कम त्रुटि-प्रवण।


9
मैं यहां ओपी के प्रति सहानुभूति रखता हूं, क्योंकि भले ही आप इन दो फायदों का हवाला देते हों, दोनों ही मामलों में आपने "कैन" शब्द का इस्तेमाल किया था। इसका मतलब है कि इस बात की कोई गारंटी नहीं है कि ये चेक वास्तव में होंगे। अब, वह व्यवहारगत अंतर प्रदर्शन-संवेदनशील परीक्षणों के लिए उपयोगी हो सकता है जिन्हें आप उत्पादन मोड में चलाने से बचना चाहते हैं, जिसके लिए हमारे पास है assert। मुझे लगता है @Nullableऔर @Nonnullउपयोगी विचार हैं, लेकिन मैं उनके पीछे और अधिक बल चाहता हूं, बजाय इसके कि हम उनके बारे में परिकल्पना करें कि उनके साथ क्या कर सकता है, जो अभी भी उनके साथ कुछ भी करने की संभावना नहीं छोड़ता है।
सेह

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

क्या मैं पूछ सकता हूँ कि AOP आप यहाँ क्या कह रहे हैं?
क्रिस .Zou

@ Chris.Zou AOP का अर्थ है पहलू उन्मुख प्रोग्रामिंग, उदाहरण के लिए AspectJ
Uwe Plonus

13

मुझे लगता है कि यह मूल प्रश्न अप्रत्यक्ष रूप से एक सामान्य सिफारिश की ओर इशारा करता है कि रन-टाइम नल-पॉइंटर चेक की अभी भी जरूरत है, भले ही @ नोनल का उपयोग किया गया हो। निम्नलिखित लिंक देखें:

जावा 8 का नया प्रकार एनोटेशन

उपरोक्त ब्लॉग में, यह अनुशंसा की जाती है कि:

वैकल्पिक प्रकार एनोटेशन रनटाइम सत्यापन के लिए एक विकल्प नहीं हैं टाइप एनोटेशन से पहले, अशक्तता या सीमा जैसी चीजों का वर्णन करने के लिए प्राथमिक स्थान javadoc में था। टाइप एनोटेशन के साथ, यह संचार संकलन-समय सत्यापन के लिए एक तरह से बायटेकोड में आता है। आपके कोड को अभी भी रनटाइम सत्यापन करना चाहिए।


1
समझ में आया, लेकिन डिफ़ॉल्ट लिंट चेक चेताते हैं कि रनटाइम नल चेक अनावश्यक हैं, जो पहली बार में इस सिफारिश को हतोत्साहित करता है।
स्वोबी

1
@swooby आमतौर पर अगर मैं सुनिश्चित करता हूं कि मेरा कोड सही है, तो मैं एक बार चेतावनी को अनदेखा कर देता हूं। उन चेतावनियों में कोई त्रुटि नहीं है।
जोनाथनज़ह

12

1.8 के अनुपालन में ग्रहण में मूल उदाहरण को संकलित करना और एनोटेशन आधारित शून्य विश्लेषण के साथ, हमें यह चेतावनी मिलती है:

    directPathToA(y);
                  ^
Null type safety (type annotations): The expression of type 'Integer' needs unchecked conversion to conform to '@NonNull Integer'

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

और एक चतुर का उपयोग करते समय @NonNullByDefaultहमें हर बार यह कहने की ज़रूरत नहीं है।

दूसरे शब्दों में: चाहे एनोटेशन "बहुत दूर तक प्रचारित करें", आपके द्वारा उपयोग किए जाने वाले टूल पर निर्भर हो सकता है, और टूल द्वारा जारी किए गए सभी चेतावनियों पर आप कितनी सख्ती से भाग लेते हैं। साथ TYPE_USE अशक्त एनोटेशन आप अंततः उपकरण के बारे में चेतावनी देने के लिए विकल्प होता है कि हर अपने कार्यक्रम में संभव एनपीई, क्योंकि nullness प्रकार प्रणाली का एक intrisic संपत्ति बन गया है।


8

मैं मानता हूं कि एनोटेशन "बहुत दूर तक प्रचार नहीं करता है"। हालांकि, मुझे प्रोग्रामर की तरफ से गलती दिखाई देती है।

मैं Nonnullएनोटेशन को प्रलेखन के रूप में समझता हूं । निम्न विधि व्यक्त करती है कि एक गैर-अशक्त तर्क के लिए (एक पूर्व शर्त के रूप में) की आवश्यकता होती है x

    public void directPathToA(@Nonnull Integer x){
        x.toString(); // do stuff to x        
    }

निम्नलिखित कोड स्निपेट में एक बग होता है। यह विधि गैर-शून्य है directPathToA()कि लागू करने के बिना कहता yहै (यह है, यह विधि के पूर्व शर्त की गारंटी नहीं है)। एक संभावना यह है कि Nonnullएनोटेशन के साथ-साथ indirectPathToA()(पूर्व शर्त का प्रचार) भी किया जाए। संभावना दो की अशक्तता के लिए जाँच करना yहै indirectPathToA()और कॉल करने से बचना है कि directPathToA()कब yअशक्त है।

    public void indirectPathToA(Integer y){
        directPathToA(y);
    }

1
प्रचार @Nonnull के लिए indirectPathToA(@Nonnull Integer y)IMHO एक बुरी बात है: आप पूर्ण कॉल स्टैक (ताकि आप एक जोड़ने पर प्रचार mainain करने की आवश्यकता होगी nullमें जांच directPathToA(), आप को बदलने के लिए की आवश्यकता होगी @Nonnullद्वारा @Nullableपूर्ण कॉल स्टैक में)। यह बड़े अनुप्रयोगों के लिए एक बड़ा रखरखाव प्रयास होगा।
जूलियन क्रोनगेल

@ नोनल एनोटेशन सिर्फ इस बात पर जोर देता है कि तर्क का शून्य-सत्यापन आपके पक्ष में है (आपको गारंटी देना होगा कि आप गैर-शून्य मान पास करते हैं)। यह विधि की जिम्मेदारी नहीं है।
अलेक्जेंडर Drobyshevsky

@ नोनल भी समझदार चीज है जब अशक्त-मूल्य इस विधि के लिए कोई मतलब नहीं है
अलेक्जेंडर Drobyshevsky

5

मैं अपनी परियोजनाओं में क्या करता हूं, "स्थिर स्थितियों और अपवादों" कोड निरीक्षण में निम्नलिखित विकल्प को सक्रिय करना है:
उन तरीकों के लिए @ योग्य विवरण का सुझाव दें जो संभवतः अशक्त लौटा सकते हैं और गैर-एनोटेट किए गए मापदंडों को दिए गए अशक्त मानों की रिपोर्ट कर सकते हैं निरीक्षण

सक्रिय होने पर, सभी गैर-एनोटेट मापदंडों को गैर-अशक्त माना जाएगा और इस प्रकार आपको अपने अप्रत्यक्ष पर एक चेतावनी भी दिखाई देगी:

clazz.indirectPathToA(null); 

और भी मजबूत जाँच के लिए चेकर फ्रेमवर्क एक अच्छा विकल्प हो सकता है (इसे अच्छा देखें ट्यूटोरियल
नोट : मैंने अभी तक इसका उपयोग नहीं किया है और जैक कंपाइलर के साथ समस्या हो सकती है: इस बगरेपोर्ट को देखें


4

जावा में मैं अमरूद के वैकल्पिक प्रकार का उपयोग करता हूँ । वास्तविक प्रकार होने के नाते आपको इसके उपयोग के बारे में संकलक गारंटी मिलती है। इसे दरकिनार करना और प्राप्त करना आसान है NullPointerException, लेकिन कम से कम विधि का हस्ताक्षर स्पष्ट रूप से यह बताता है कि यह एक तर्क के रूप में क्या उम्मीद करता है या क्या वापस आ सकता है।


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

यदि आप JDK 8 या नए लक्ष्य कर रहे हैं, तो java.util.Optionalअमरूद की कक्षा के बजाय उपयोग करना पसंद करते हैं । अंतर पर विवरण के लिए अमरूद के नोट / तुलना देखें।
एंड्रयूएफ

1
"अशक्त चेकों को उपस्थिति के लिए चेक के साथ बदल दिया जाता है जो बिंदु को याद करता है" क्या आप विस्तृत कर सकते हैं, फिर, बिंदु क्या है ? यह मेरी राय में, वैकल्पिक का एकमात्र कारण नहीं है , लेकिन यह निश्चित रूप से अब तक का सबसे बड़ा और सर्वश्रेष्ठ है।
scubbo

4

यदि आप कोटलिन का उपयोग करते हैं, तो यह इसके संकलक में इन अशक्तता एनोटेशन का समर्थन करता है और आपको एक अशक्त विधि से शून्य से गुजरने से रोकता है, जिसके लिए गैर-अशक्त तर्क की आवश्यकता होती है। घटना हालांकि यह सवाल मूल रूप से जावा पर लक्षित था, मैंने इस कोटलिन फीचर का उल्लेख किया है क्योंकि यह विशेष रूप से इन जावा एनोटेशन पर लक्षित है और सवाल था "क्या इन एनोटेशन को और अधिक सख्ती से लागू करने और / या आगे प्रचार करने का कोई तरीका है ?" और यह सुविधा इन एनोटेशन को और अधिक सख्ती से लागू करती है

@NotNullएनोटेशन का उपयोग करते हुए जावा वर्ग

public class MyJavaClazz {
    public void foo(@NotNull String myString) {
        // will result in an NPE if myString is null
        myString.hashCode();
    }
}

कोटलिन ने जावा क्लास को कॉल किया और @NotNull के साथ एनोटेट तर्क के लिए अशक्त हो गया

class MyKotlinClazz {
    fun foo() {
        MyJavaClazz().foo(null)
    }
}  

कोटलिन संकलक त्रुटि @NotNullएनोटेशन को लागू करता है ।

Error:(5, 27) Kotlin: Null can not be a value of a non-null type String

देखें: http://kotlinlang.org/docs/reference/java-interop.html#nullability-annotations


3
सवाल जावा को उसके पहले टैग के अनुसार संबोधित करता है, न कि कोटलिन को।
सेह

1
@ इस अपडेट के लिए यह प्रश्न प्रासंगिक क्यों है, इसके लिए अपडेट देखें।
माइक रायलैंडर

2
काफी उचित। यह कोटलिन की एक अच्छी विशेषता है। मुझे नहीं लगता कि यह जावा के बारे में जानने के लिए यहां आने वालों को संतुष्ट करने वाला है।
सेह

लेकिन एक्सेस myString.hashCode()करना अभी भी NPE को फेंक रहा होगा, भले ही @NotNullपैरामीटर में नहीं जोड़ा गया हो। तो इसे जोड़ने के बारे में अधिक विशिष्ट क्या है?
kAmol

@kAmol यहां अंतर यह है कि कोटलिन का उपयोग करते समय, आपको रनटाइम त्रुटि के बजाय एक संकलन समय त्रुटि मिलेगी । एनोटेशन यह सूचित करने के लिए है कि आपको डेवलपर को यह सुनिश्चित करने की आवश्यकता है कि एक अशक्त पास नहीं है। यह एक अशक्त को रनटाइम में पारित होने से नहीं रोकेगा, लेकिन यह आपको उस कोड को लिखने से रोकेगा जो इस पद्धति को एक अशक्त के साथ कहता है (या एक समारोह के साथ कि अशक्त लौट सकते हैं)।
बजे माइक रायलैंडर

-4

चूंकि जावा 8 नई सुविधा वैकल्पिक है, इसलिए आपको अपने स्वयं के कोड में @Nullable या @Notnull का उपयोग नहीं करना चाहिए । नीचे दिए गए उदाहरण को लें:

public void printValue(@Nullable myValue) {
    if (myValue != null) {
        System.out.print(myValue);
    } else {
        System.out.print("I dont have a value");
}

इसे फिर से लिखा जा सकता है:

public void printValue(Optional<String> myValue) {
    if (myValue.ifPresent) {
        System.out.print(myValue.get());
    } else {
        System.out.print("I dont have a value");
}

एक वैकल्पिक बल का उपयोग करके आप अशक्त मूल्य की जांच कर सकते हैं । उपरोक्त कोड में, आप केवल कॉल करके मान तक पहुँच सकते हैंget विधि ।

एक और फायदा यह है कि कोड अधिक पठनीय हो जाता है । जावा 9 ifPresentOrElse को जोड़ने के साथ , फ़ंक्शन को इस प्रकार भी लिखा जा सकता है:

public void printValue(Optional<String> myValue) {
    myValue.ifPresentOrElse(
        v -> System.out.print(v),
        () -> System.out.print("I dont have a value"),
    )
}

यहां तक ​​कि Optional, अभी भी कई लाइब्रेरी और चौखटे हैं, जो इन एनोटेशन का उपयोग करते हैं जैसे कि वैकल्पिक उपयोग करने के लिए अपडेट किए गए संस्करणों के साथ अपने सभी निर्भरता को अद्यतन / प्रतिस्थापित करना संभव नहीं है। Optionalहालाँकि, उन परिस्थितियों में मदद कर सकता है जहाँ आप अपने स्वयं के कोड के भीतर नल का उपयोग करते हैं।
माइक रायलैंडर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.