एक विधि को स्थिर घोषित करने से क्या लाभ है


94

मैं हाल ही में ग्रहण में अपनी चेतावनियों के माध्यम से देख रहा हूँ और इस पर आता हूँ:

स्थिर चेतावनी

यह संकलक चेतावनी देगा यदि विधि को स्थिर के रूप में घोषित किया जा सकता है।

[संपादित करें] निजी और अंतिम पर तनाव के साथ, ग्रहण सहायता के भीतर सटीक उद्धरण:

सक्षम होने पर, कंपाइलर उन तरीकों के लिए एक त्रुटि या चेतावनी जारी करेगा जो निजी या अंतिम हैं और जो केवल स्थैतिक सदस्यों को संदर्भित करते हैं।

हां मुझे पता है कि मैं इसे बंद कर सकता हूं, लेकिन मैं इसे चालू करने का कारण जानना चाहता हूं ?

स्थैतिक के रूप में हर विधि को संभव घोषित करना एक अच्छी बात क्यों होगी?

क्या इससे कोई प्रदर्शन लाभ मिलेगा? (एक मोबाइल डोमेन में)

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

दिन के अंत में मुझे बस इस 'अनदेखी' को बंद कर देना चाहिए या मुझे 100+ चेतावनियों को ठीक करना चाहिए जो उसने मुझे दी है?

क्या आपको लगता है कि यह केवल अतिरिक्त कीवर्ड हैं जो कोड को गंदा करते हैं, क्योंकि कंपाइलर इन तरीकों को वैसे भी इनलाइन कर देगा? (इस तरह की तरह आप हर उस वेरिएबल को घोषित नहीं करते जिसे आप फाइनल कर सकते हैं लेकिन आप कर सकते हैं )।


अनिश्चित लेकिन यह केवल एक प्रोग्रामिंग सहायता के रूप में देखा जा सकता है। चेतावनी केवल चीजों को देखने के संकेत हैं।
जेम्स पी।

1
मैं इन विधियों के प्रदर्शन के प्रकार की तरह उत्सुक हूं। अगर इनमें से बहुत सारे हैं तो शायद कुछ बहुत सही नहीं किया गया था।
अर्जुनशंकर

जवाबों:


132

जब भी आप कोई विधि लिखते हैं, तो आप दिए गए दायरे में एक अनुबंध पूरा करते हैं। दायरा जितना छोटा होता है, मौका उतना ही छोटा होता है कि आप बग लिख देते हैं।

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

और इसके अलावा, यह आपके कोड को पढ़ने वाले लोगों को अनुबंध की प्रकृति को समझने में मदद करेगा।

इसीलिए यह एक विधि घोषित करना अच्छा माना जाता है staticजब यह वास्तव में एक स्थिर अनुबंध को लागू कर रहा हो।

कुछ मामलों में, आपकी विधि का अर्थ केवल आपके वर्ग के उदाहरण के सापेक्ष कुछ है, और ऐसा होता है कि इसका कार्यान्वयन वास्तव में किसी भी गैर-स्थिर क्षेत्र या उदाहरण का उपयोग नहीं करता है। ऐसे मामलों में, आप विधि को चिह्नित नहीं करेंगे static

आप staticकीवर्ड का उपयोग कहां नहीं करेंगे इसके उदाहरण :

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

1
+1 अपने आप को पैर में गोली मारने के अवसरों को कम करने के बारे में, और एक विधि को समझने के लिए आपको कितना कम करना है।
पीटर लॉरी

7
इसके अलावा, आपको एक स्वतंत्र, स्व-निहित फ़ंक्शन को कॉल करने के लिए केवल एक उदाहरण प्राप्त करने की आवश्यकता नहीं है।
मार्को टोपोलनिक

1
इसलिए अन्य दुनिया में, कोई भी विधि जो उदाहरण चर का उपयोग नहीं कर रही है, उसे स्थिर के रूप में घोषित किया जाना चाहिए? मैंने हमेशा सोचा था कि विधियों को केवल तभी स्थिर होना चाहिए जब यह वांछनीय हो (जैसे कि उपयोगिता के तरीके)।
पेट्र मेंसिक

18
@PetrMensik यदि किसी privateविधि को स्थिर घोषित किया जा सकता है, तो लगभग हमेशा होना चाहिए। किसी अन्य पहुंच स्तर के लिए, विचार करने के लिए अन्य कारक हैं, जैसे गतिशील प्रेषण।
मार्को टोपोलनिक

1
@JamesPoulson का मतलब है कि डायनेमिक विधि प्रेषण, object.method()कॉल करने की विधि का चयन करने के लिए बाहर निकलते समय होने वाली बात ।
मार्को टोपोलनिक

15

यहां अनुकूलन के साथ कोई अवधारणा नहीं है।

एक staticविधि यह है staticक्योंकि आप स्पष्ट रूप से यह घोषित करते हैं कि यह पद्धति किसी भी उदाहरण पर संलग्न वर्ग पर निर्भर नहीं करती है क्योंकि उसे इसकी आवश्यकता नहीं है। ताकि ग्रहण की चेतावनी, जैसा कि प्रलेखन में कहा गया है:

सक्षम होने पर, कंपाइलर उन तरीकों के लिए एक त्रुटि या चेतावनी जारी करेगा जो निजी या अंतिम हैं और जो केवल स्थैतिक सदस्यों को संदर्भित करते हैं।

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


7

मुझे प्रदर्शन पर कोई जानकारी नहीं है, मुझे लगता है कि यह मामूली रूप से बेहतर है, क्योंकि कोड को प्रकार के आधार पर गतिशील प्रेषण करने की आवश्यकता नहीं है।

हालांकि, स्थैतिक तरीकों में रिफैक्टिंग के खिलाफ एक बहुत मजबूत तर्क यह है कि वर्तमान में स्थैतिक का उपयोग करना बुरा अभ्यास माना जाता है। स्थैतिक तरीके / चर एक वस्तु उन्मुख भाषा में अच्छी तरह से एकीकृत नहीं होते हैं और साथ ही, ठीक से परीक्षण करने के लिए कठिन होते हैं। यही कारण है कि कुछ नई भाषाओं ने स्थिर तरीकों / चर की अवधारणा को पूरी तरह से भुनाया, या इसे इस तरह से भाषा में आंतरिक रूप से बेहतर बनाने की कोशिश की, जो ओओ के साथ बेहतर खेलती है (जैसे कि स्काला में ऑब्जेक्ट्स)।

अधिकांश समय, आपको उन कार्यों को लागू करने के लिए स्थिर तरीकों की आवश्यकता होती है जो केवल एक इनपुट के रूप में मापदंडों का उपयोग कर रहे हैं और एक आउटपुट का उत्पादन कर रहे हैं (उदाहरण के लिए उपयोगिता / सहायक कार्य) आधुनिक भाषाओं में, एक प्रथम श्रेणी फ़ंक्शन अवधारणा है जो अनुमति देती है, इसलिए स्थिर आवश्यकता नहीं है। जावा 8 में लैम्ब्डा के भावों को एकीकृत किया जाएगा, इसलिए हम इस दिशा में आगे बढ़ रहे हैं।


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

5
परीक्षण करने योग्य टिप्पणी शायद स्थैतिक विधि की ओर सीधे ध्यान केंद्रित नहीं की गई थी, लेकिन स्थैतिक तरीकों का मजाक उड़ाना अधिक कठिन है, इसलिए वे उन विधियों के परीक्षण को जटिल बनाते हैं जो स्थैतिक तरीकों को कहते हैं।
बुह

2
यदि आप एक शक्तिशाली मॉकिंग फ्रेमवर्क जैसे JMockit , PowerMock , या Groovy का उपयोग करते हैं, तो स्टैटिक विधियाँ मॉक करना आसान है ।
जेफ ओल्सन

ये ऐसे private staticतरीके हैं जिससे आपको उनका मजाक
उड़ाने की

3

1. घोषणा विधिstaticमामूली प्रदर्शन लाभ देता है, लेकिन जो अधिक उपयोगी है, वह इसे हाथ में एक वस्तु उदाहरण के बिना उपयोग करने की अनुमति देता है (कारखाने की विधि के बारे में उदाहरण के लिए सोचें या एक सिंगलटन प्राप्त करना)। यह विधि की प्रकृति को बताने के दस्तावेजी उद्देश्य को भी पूरा करता है। इस दस्तावेजी उद्देश्य को नजरअंदाज नहीं किया जाना चाहिए, क्योंकि यह एपीआई के कोड और उपयोगकर्ताओं के पाठकों के लिए विधि की प्रकृति के बारे में तत्काल संकेत देता है और मूल प्रोग्रामर के लिए सोच के एक उपकरण के रूप में भी कार्य करता है - इसका अर्थ के बारे में स्पष्ट होना आप भी सीधे सोचते हैं और बेहतर गुणवत्ता वाले कोड का निर्माण करते हैं (मुझे लगता है कि मेरे व्यक्तिगत अनुभव के आधार पर, लेकिन लोग अलग हैं)। उदाहरण के लिए, यह एक प्रकार पर काम करने वाले तरीकों और तरीकों के बीच अंतर करने के लिए तर्कसंगत और इसलिए वांछनीय है कि प्रकार के अनुसार (जैसा कि कहा गया है)जॉन स्कीट ने C # प्रश्न के लिए अपनी टिप्पणी में )।

अभी तक staticतरीकों के लिए एक और उपयोग मामला प्रक्रियात्मक प्रोग्रामिंग इंटरफ़ेस की नकल करना है। java.lang.System.println()कक्षा के बारे में सोचें और उसमें मौजूद तरीकों और विशेषताओं के बारे में सोचें । क्लास java.lang.Systemको इंस्टेंटेबल ऑब्जेक्ट के बजाय ग्रुपिंग नाम स्पेस की तरह उपयोग किया जाता है।

2. ग्रहण (या किसी भी अन्य क्रमादेशित या अन्य प्रकार के - जैव-रासायनिक या गैर-जैव-रासायनिक - इकाई) कैसे सुनिश्चित करें कि किस विधि को स्थिर के रूप में घोषित किया जा सकता है? यहां तक ​​कि अगर एक बेस क्लास इंस्टेंस वेरिएबल तक नहीं पहुंच रहा है या गैर-स्टेटिक तरीकों को कॉल कर रहा है, तो इनहेरिटेंस के मैकेनिज्म से चीजें बदल सकती हैं। केवल अगर विधि को उपवर्ग विरासत में प्राप्त नहीं किया जा सकता है, तो क्या हम 100% निश्चितता के साथ दावा कर सकते हैं कि विधि वास्तव में घोषित की जा सकती है static। होने के दो मामलों में एक विधि को ओवरराइड करना बिल्कुल असंभव है

  1. private (कोई उपवर्ग इसका सीधा उपयोग नहीं कर सकता है और सिद्धांत रूप में इसके बारे में जानता भी नहीं है), या
  2. final (उपवर्ग द्वारा सुलभ होने पर भी, डेटा या फ़ंक्शंस को संदर्भित करने के लिए विधि को बदलने का कोई तरीका नहीं है)।

इसलिए ग्रहण विकल्प का तर्क।

3. मूल पोस्टर यह भी पूछता है: " एक विधि को स्थिर के रूप में इंगित करते हुए, मुझे लगता है कि आप किसी भी उदाहरण चर का उपयोग नहीं करते हैं इसलिए इसे एक शैली शैली वर्ग में ले जाया जा सकता है? " यह एक बहुत अच्छा बिंदु है। कभी-कभी इस तरह के डिज़ाइन परिवर्तन को चेतावनी द्वारा इंगित किया जाता है।

यह एक बहुत उपयोगी विकल्प है, जिसे मैं व्यक्तिगत रूप से सक्षम करने के लिए सुनिश्चित करूंगा, क्या मैं ग्रहण का उपयोग कर सकता हूं और क्या मैं जावा में प्रोग्राम कर सकता हूं।


1

सैमुअल का जवाब देखें कि विधि का दायरा कैसे बदलता है। मुझे लगता है, यह एक विधि को स्थिर बनाने का मुख्य पहलू है।

आपने प्रदर्शन के बारे में भी पूछा:

एक छोटा प्रदर्शन लाभ हो सकता है, क्योंकि एक स्थिर विधि के लिए कॉल को पैरामीटर के रूप में इस "संदर्भ" के निहितार्थ की आवश्यकता नहीं है।

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


1

Android प्रदर्शन दिशानिर्देशों से:

वर्चुअल पर स्टेटिक को प्राथमिकता दें यदि आपको किसी ऑब्जेक्ट के फ़ील्ड तक पहुंचने की आवश्यकता नहीं है, तो अपनी विधि को स्थिर बनाएं। इनवोकेशन लगभग 15% -20% तेज़ी से होगा। यह अच्छा अभ्यास भी है, क्योंकि आप विधि हस्ताक्षर से बता सकते हैं कि विधि को कॉल करने से वस्तु की स्थिति में परिवर्तन नहीं हो सकता है।

http://developer.android.com/training/articles/perf-tips.html#PreferStatic


"कि विधि को कॉल करने से वस्तु की स्थिति में परिवर्तन नहीं हो सकता है" - अविश्वसनीय रूप से भ्रामक है। मैं आपके बारे में नहीं जानता, लेकिन मैं निश्चित रूप से किसी वस्तु के राज्य का एक हिस्सा होने के लिए एक वर्ग की स्थैतिक विशेषताओं पर विचार करता हूं।
एडम पार्किन

0

ठीक है, ग्रहण के दस्तावेज में प्रश्न में चेतावनी के बारे में कहा गया है:

विधि स्थिर हो सकती है

सक्षम होने पर, कंपाइलर उन तरीकों के लिए एक त्रुटि या चेतावनी जारी करेगा जो निजी या अंतिम हैं और जो केवल स्थैतिक सदस्यों को संदर्भित करते हैं

मुझे लगता है कि यह बहुत कुछ कहता है। यदि विधि निजी और अंतिम है और केवल स्थैतिक सदस्यों को संदर्भित करती है, तो विचाराधीन विधि को भी स्थैतिक घोषित किया जा सकता है और इसके द्वारा यह स्पष्ट किया जाता है कि हम केवल इससे स्थिर सामग्री तक पहुँचने का इरादा रखते हैं।

मुझे नहीं लगता कि इसके पीछे कोई और रहस्यमय कारण है।


0

मुझे गति के अंतर के लिए कुछ नंबर याद आ रहे थे। इसलिए मैंने उन्हें बेंचमार्क करने की कोशिश की, जो इतना आसान नहीं था: जावा लूप कुछ रन / JIT की गलती के बाद धीमा हो जाता है?

मैंने अंततः कैलीपर का उपयोग किया और परिणाम मेरे परीक्षणों को हाथ से चलाने के समान हैं:

स्थिर / गतिशील कॉल के लिए कोई औसत दर्जे का अंतर नहीं है। कम से कम Linux / AMD64 / Java7 के लिए नहीं।

कैलीपर के परिणाम यहां दिए गए हैं: https://microbenchmarks.appspot.com/runs/1426eac9-36ca-48f0-980f-0106af064e8f#r:scenario.benchenSpec.methodName,scenario.vmSpec.options.CMSLargeXcotouplus.info/ CMSLargeSplitSurplusPercent, scenario.vmSpec.options.CMSSmallCoalSurplusPercent, scenario.vmSpec.options.CMSSmallSplitSurplusPercent, scenario.vmSpec.options.FLSLargestBlockCoalesceProximity, scenario.vmSpec.options.G1ConcMarkStepDurationMillis

और मेरे अपने परिणाम हैं:

Static: 352 ms
Dynamic: 353 ms
Static: 348 ms
Dynamic: 349 ms
Static: 349 ms
Dynamic: 348 ms
Static: 349 ms
Dynamic: 344 ms

कैलिपर टेस्ट क्लास थी:

public class TestPerfomanceOfStaticMethodsCaliper extends Benchmark {

    public static void main( String [] args ){

        CaliperMain.main( TestPerfomanceOfStaticMethodsCaliper.class, args );
    }

    public int timeAddDynamic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addDynamic( 1, i );
        }
        return r;
    }

    public int timeAddStatic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addStatic( 1, i );
        }
        return r;
    }

    public int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}

और मेरा अपना टेस्ट क्लास था:

public class TestPerformanceOfStaticVsDynamicCalls {

    private static final int RUNS = 1_000_000_000;

    public static void main( String [] args ) throws Exception{

        new TestPerformanceOfStaticVsDynamicCalls().run();
    }

    private void run(){

        int r=0;
        long start, end;

        for( int loop = 0; loop<10; loop++ ){

            // Benchmark

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addStatic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Static: " + ( end - start ) + " ms" );

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addDynamic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Dynamic: " + ( end - start ) + " ms" );

            // Do something with r to keep compiler happy
            System.out.println( r );

        }

    }

    private int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}

विभिन्न Android उपकरणों और संस्करणों पर परिणाम देखना दिलचस्प होगा
Blundell

हाँ। मैंने सोचा था कि आपकी रुचि होगी :) लेकिन जब से आप एंड्रॉइड सॉफ़्टवेयर का निर्माण कर रहे हैं और संभवत: आपके देव स्टेशन से जुड़ा हुआ एक एंड्रॉइड डिवाइस है, तो मेरा सुझाव है कि आप बस कोड चुनें, इसे चलाएं और परिणाम साझा करें?
Scheintod

-2

आप जिन तरीकों को स्थिर घोषित कर सकते हैं, वे हैं जिन्हें तात्कालिकता की आवश्यकता नहीं है, जैसे कि

public class MyClass
{
    public static string InvertText(string text)
    {
        return text.Invert();
    }
}

फिर आप उस वर्ग को बिना किसी अन्य वर्ग के बदले में कॉल आउट कर सकते हैं।

public class MyClassTwo
{
    public void DoSomething()
    {
        var text = "hello world";
        Console.Write(MyClass.InvertText(text));
    }
}

... लेकिन यह कुछ ऐसा है जो आप शायद पहले से ही जानते हैं। यह आपको प्रति से अधिक वास्तविक लाभ नहीं देता है, यह स्पष्ट करने के अलावा कि कोई भी उदाहरण चर का उपयोग नहीं करता है।

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


1
वह भाषा कौन सी है? क्या वह उदाहरण संकलन करता है? यहां कुछ भी स्थिर नहीं है।
पायोत्र पेरक

वास्तव में, लगता है कि मैं InvertText विधि से 'स्थिर' भूल गया। और यह c # पर आधारित एक उदाहरण है
NeroS
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.