मुझे जावा में विधि पैरामीटर पर "अंतिम" कीवर्ड का उपयोग क्यों करना चाहिए?


381

मुझे समझ नहीं आ रहा है कि विधि मापदंडों पर उपयोग किए जाने पर finalकीवर्ड वास्तव में कितना आसान है।

यदि हम अनाम कक्षाओं, पठनीयता और आशय की घोषणा के उपयोग को बाहर करते हैं तो यह मेरे लिए लगभग बेकार लगता है।

यह सुनिश्चित करना कि कुछ डेटा स्थिर रहता है, उतना मजबूत नहीं है जितना लगता है।

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

  • यदि हम संदर्भ द्वारा एक पैरामीटर पास कर रहे हैं, तो संदर्भ स्वयं एक स्थानीय चर है और यदि संदर्भ को विधि के भीतर से बदल दिया जाता है, तो इससे विधि के दायरे के बाहर कोई प्रभाव नहीं पड़ेगा।

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

public void testNullify() {
    Collection<Integer> c  = new ArrayList<Integer>();      
    nullify(c);
    assertNotNull(c);       
    final Collection<Integer> c1 = c;
    assertTrue(c1.equals(c));
    change(c);
    assertTrue(c1.equals(c));
}

private void change(Collection<Integer> c) {
    c = new ArrayList<Integer>();
}

public void nullify(Collection<?> t) {
    t = null;
}

101
शब्दावली पर एक त्वरित बिंदु - जावा में पास-पास-संदर्भ नहीं है। इसमें मूल्य द्वारा संदर्भ दिया गया है जो समान बात नहीं है। संदर्भ शब्दार्थ द्वारा सही पास के साथ आपके कोड के परिणाम भिन्न होंगे।
जॉन स्कीट

6
संदर्भ द्वारा पास और मान के संदर्भ के बीच अंतर क्या है?
नोबल उत्थान

सी अंतर में उस अंतर का वर्णन करना आसान है (कम से कम मेरे लिए)। यदि मैं एक पॉइंटर को एक विधि से पास करता हूँ जैसे: <code> int foo (int bar) </ code>, तो वह पॉइंटर मान से पास किया जा रहा है। मतलब यह कॉपी किया जाता है, इसलिए अगर मैं उस तरीके के अंदर कुछ करता हूं, जैसे <code> free (bar); bar = malloc (...); </ code> तो मैंने अभी बहुत बुरा काम किया है। मुफ्त कॉल वास्तव में मेमोरी का हिस्सा होने का संकेत देगा (इसलिए जो भी पॉइंटर मैंने पास किया है वह अब झूल रहा है)। हालांकि, <code> int foo (int & bar) </ bar> का अर्थ है कि कोड मान्य है और इसमें दिए गए पॉइंटर का मान बदल जाएगा।
१०:

1
पहले वाला माना जाता है int foo(int* bar)और आखिरी वाला int foo(int* &bar)। उत्तरार्द्ध संदर्भ से एक सूचक पारित कर रहा है, पूर्व मूल्य से एक संदर्भ पारित कर रहा है।
जारसलान

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

जवाबों:


239

एक वैरिएबल रिअसाइनमेंट रोकें

हालांकि ये उत्तर बौद्धिक रूप से दिलचस्प हैं, मैंने छोटा सरल उत्तर नहीं पढ़ा है:

जब आप चाहते हैं कि कीवर्ड अंतिम का उपयोग करें एक चर को फिर से एक अलग वस्तु को सौंपा जाने से रोकने के लिए कंपाइलर चाहिए।

चर एक स्थिर चर, सदस्य चर, स्थानीय चर, या तर्क / पैरामीटर चर है या नहीं, प्रभाव पूरी तरह से एक ही है।

उदाहरण

कार्रवाई में प्रभाव देखते हैं।

इस सरल विधि पर विचार करें, जहां दो चर ( arg और x ) दोनों को अलग-अलग वस्तुओं को फिर से सौंपा जा सकता है।

// Example use of this method: 
//   this.doSomething( "tiger" );
void doSomething( String arg ) {
  String x = arg;   // Both variables now point to the same String object.
  x = "elephant";   // This variable now points to a different String object.
  arg = "giraffe";  // Ditto. Now neither variable points to the original passed String.
}

स्थानीय चर को अंतिम रूप में चिह्नित करें । यह एक संकलक त्रुटि के परिणामस्वरूप होता है।

void doSomething( String arg ) {
  final String x = arg;  // Mark variable as 'final'.
  x = "elephant";  // Compiler error: The final local variable x cannot be assigned. 
  arg = "giraffe";  
}

इसके बजाय, हम अंतिम के रूप में पैरामीटर चर को चिह्नित करते हैं । यह भी एक संकलक त्रुटि में परिणाम है।

void doSomething( final String arg ) {  // Mark argument as 'final'.
  String x = arg;   
  x = "elephant"; 
  arg = "giraffe";  // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
}

कहानी का नैतिक:

यदि आप एक चर को हमेशा एक ही वस्तु पर इंगित करना चाहते हैं, तो चर को अंतिम रूप से चिह्नित करें ।

कभी भी पुन: तर्क न करें

अच्छी प्रोग्रामिंग प्रैक्टिस (किसी भी भाषा में) के रूप में, आपको कॉलिंग विधि द्वारा पारित ऑब्जेक्ट के अलावा किसी ऑब्जेक्ट के पैरामीटर / तर्क चर को फिर से असाइन नहीं करना चाहिए । ऊपर के उदाहरणों में, किसी को कभी भी लाइन नहीं लिखनी चाहिए arg =। चूंकि मानव गलती करते हैं, और प्रोग्रामर मानव होते हैं, इसलिए संकलक से हमारी सहायता करने के लिए कहें। प्रत्येक पैरामीटर / तर्क चर को 'अंतिम' के रूप में चिह्नित करें ताकि संकलक ऐसे किसी भी पुन: असाइनमेंट को ढूंढ और चिह्नित कर सके।

सिंहावलोकन करने पर

जैसा कि अन्य उत्तरों में उल्लेख किया गया है ... किसी सरणी के अंत को पढ़ने के रूप में गूंगा गलतियों से बचने के लिए प्रोग्रामर की मदद करने के जावा के मूल डिजाइन लक्ष्य को देखते हुए, जावा को सभी पैरामीटर / तर्क चर को 'अंतिम' के रूप में स्वचालित रूप से लागू करने के लिए डिज़ाइन किया जाना चाहिए था। दूसरे शब्दों में, तर्क परिवर्तनशील नहीं होने चाहिए । लेकिन उस समय दृष्टि 20/20 की थी और उस समय जावा डिजाइनरों के हाथ पूरे थे।

तो, हमेशा finalसभी तर्कों में जोड़ें ?

क्या हमें finalघोषित किए जा रहे प्रत्येक और हर विधि पैरामीटर को जोड़ना चाहिए ?

  • सिद्धांत रूप में, हाँ।
  • व्यवहार में, नहीं।
    finalकेवल तभी जोड़ें जब विधि का कोड लंबा या जटिल हो, जहां तर्क स्थानीय या सदस्य चर के लिए गलत हो सकता है और संभवतः फिर से सौंपा जा सकता है।

यदि आप एक तर्क को फिर से निर्दिष्ट नहीं करने के अभ्यास में खरीदते हैं, तो आप finalप्रत्येक में एक जोड़ने के लिए इच्छुक होंगे । लेकिन यह थकाऊ है और घोषणा को पढ़ने के लिए थोड़ा कठिन बना देता है।

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

पूर्णता के लिए एक और मामला जोड़ा गया

public class MyClass {
    private int x;
    //getters and setters
}

void doSomething( final MyClass arg ) {  // Mark argument as 'final'.

   arg =  new MyClass();  // Compiler error: The passed argument variable arg  cannot be re-assigned to another object.

   arg.setX(20); // allowed
  // We can re-assign properties of argument which is marked as final
 }

23
"अच्छी प्रोग्रामिंग प्रैक्टिस (किसी भी भाषा में) के रूप में, आपको कभी भी एक पैरामीटर / तर्क चर नहीं देना चाहिए [..]" क्षमा करें, मुझे वास्तव में आपको इस पर कॉल करना होगा। पुन: असाइन करने वाले तर्क, जावास्क्रिप्ट जैसी भाषाओं में मानक अभ्यास है, जहां पारित किए गए तर्कों की मात्रा (या यहां तक ​​कि अगर कोई पारित हो) विधि हस्ताक्षर द्वारा निर्धारित नहीं है। जैसे कि एक हस्ताक्षर दिया गया है: "फ़ंक्शन का कहना है (msg)" लोग सुनिश्चित करेंगे कि तर्क 'msg' को सौंपा गया है, जैसे:: Ms = msg || 'Hello World!'; "। दुनिया में सबसे अच्छा जावास्क्रिप्ट प्रोग्रामर आपके अच्छे अभ्यास को तोड़ रहे हैं। बस jQuery स्रोत पढ़ें।
स्टिजेन डे विट

37
@StijndeWitt आपका उदाहरण तर्क चर को आश्वस्त करने की बहुत समस्या दर्शाता है। आप बदले में प्राप्त किए गए कुछ भी नहीं के साथ जानकारी खो देते हैं: (ए) आपने पास किया मूल मूल्य खो दिया है, (बी) आपने कॉलिंग विधि का इरादा खो दिया है (क्या कॉलर 'हैलो वर्ल्ड!' या हमने डिफ़ॉल्ट रूप से पास किया है)। ए और बी दोनों परीक्षण, लंबे कोड के लिए उपयोगी होते हैं, और जब मूल्य को बाद में बदल दिया जाता है। मैं अपने बयान के साथ खड़ा हूं: arg vars को कभी भी पुन: असाइन नहीं किया जाना चाहिए । आपका कोड होना चाहिए: message = ( msg || 'Hello World"' )। एक अलग संस्करण का उपयोग करने का कोई कारण नहीं है। केवल लागत स्मृति के कुछ बाइट्स है।
बेसिल बोर्के

9
@ बासिल: यह अधिक कोड (बाइट्स में) और जावास्क्रिप्ट में है जो गिनती करता है। भारी। कई बातों के साथ यह राय आधारित है। इस प्रोग्रामिंग अभ्यास को पूरी तरह से अनदेखा करना और अभी भी उत्कृष्ट कोड लिखना पूरी तरह से संभव है। एक व्यक्ति की प्रोग्रामिंग प्रैक्टिस इसे हर किसी का अभ्यास नहीं बनाती है। यह सब तुम खड़े हो जाओ, मैं इसे अलग तरह से लिखने के लिए चुनते हैं। क्या यह मुझे एक बुरा प्रोग्रामर, या मेरा कोड खराब कोड बनाता है?
स्टिजन डी विट

14
message = ( msg || 'Hello World"' )बाद में गलती से मेरे द्वारा उपयोग किए जाने वाले जोखिमों का उपयोग करना msg। जब मैं अनुबंध करना चाहता हूं तो "नो / नल / अपरिभाषित आर्ग के साथ व्यवहार गुजरने से अप्रभेद्य है "Hello World"", यह फ़ंक्शन में जल्दी करने के लिए एक अच्छा प्रोग्रामिंग अभ्यास है। [यह बिना किसी पुनर्मूल्यांकन के शुरू किया जा सकता है, if (!msg) return myfunc("Hello World");लेकिन इसके साथ कई तर्क भी मिलते हैं।] दुर्लभ मामलों में जहां फ़ंक्शन में तर्क को ध्यान रखना चाहिए कि क्या डिफ़ॉल्ट का उपयोग किया गया था, मैं विशेष रूप से एक विशेष प्रहरी मूल्य (अधिमानतः सार्वजनिक) निर्दिष्ट करता हूं ।
बेनी चेर्नियाव्स्की-पास्किन

6
@ BeniCherniavsky-Paskin आपके द्वारा वर्णित जोखिम केवल messageऔर के बीच समानता के कारण है msg। लेकिन अगर वह इसे कुछ ऐसा processedMsgया कुछ और कहेंगे जो अतिरिक्त संदर्भ प्रदान करता है - गलती की संभावना बहुत कम है। उस पर ध्यान केंद्रित करें जो वह कहता है " नहीं " पर वह कैसे कहता है। ;)
अल्फासिन

230

कभी-कभी यह स्पष्ट होना अच्छा होता है (पठनीयता के लिए) कि चर नहीं बदलता है। यहां एक सरल उदाहरण दिया गया है जहां प्रयोग से finalकुछ संभावित सिरदर्द से बचा जा सकता है:

public void setTest(String test) {
    test = test;
}

यदि आप एक सेटर पर 'इस' कीवर्ड को भूल जाते हैं, तो आप जिस वेरिएबल को सेट करना चाहते हैं वह सेट नहीं होता है। हालांकि, यदि आपने finalपैरामीटर पर कीवर्ड का उपयोग किया है , तो संकलन समय पर बग को पकड़ा जाएगा।


65
Btw आप देखेंगे चेतावनी "चर परीक्षण के लिए असाइनमेंट का कोई प्रभाव नहीं है" वैसे भी
AvrDragon

12
@ArrDragon लेकिन, हम चेतावनी को भी अनदेखा कर सकते हैं। इसलिए, हमेशा कुछ ऐसा करना बेहतर होता है जो हमें संकलन त्रुटि की तरह आगे जाने से रोके, जिसे हम अंतिम कीवर्ड का उपयोग करके प्राप्त करेंगे।
सुमित देसाई

10
@ArrDragon यह विकास के माहौल पर निर्भर है। जब तक आप बुरी आदतों को विकसित नहीं करना चाहते, तब तक आपको इस तरह से सामान पकड़ने के लिए आईडीई पर निर्भर नहीं होना चाहिए।
b1nary.atr0phy

25
@ b1naryatr0phy वास्तव में यह एक संकलक चेतावनी है, न कि केवल
IDE-

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

127

हां, अनाम कक्षाओं, पठनीयता और आशय की घोषणा को छोड़कर यह लगभग बेकार है। हालांकि वे तीन चीजें बेकार हैं?

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

आपकी बात अधिक महत्वपूर्ण होगी यदि कोई वास्तव में यह दावा कर रहा था कि उसने डेटा को इस तरह से स्थिर रखा है जो ऐसा नहीं करता है - लेकिन मैं इस तरह के दावों को देखकर याद नहीं रख सकता। क्या आप यह सुझाव दे रहे हैं कि डेवलपर्स का एक महत्वपूर्ण निकाय यह सुझाव दे रहा है कि वास्तव finalमें इससे अधिक प्रभाव पड़ता है?

संपादित करें: मुझे वास्तव में एक मोंटी पायथन संदर्भ के साथ इस सब को सम्‍मिलित करना चाहिए था; सवाल कुछ हद तक यह पूछने के समान है कि "रोमन ने हमारे लिए क्या किया है?"


17
लेकिन अपने भाई के साथ क्रिटिश को खुश करने के लिए, उन्होंने हमारे लिए क्या किया ? =)
जेम्स स्कैच

युवाल। अजीब बात है! मुझे लगता है कि शांति हो सकती है भले ही यह तलवार के ब्लेड से लागू हो!
गोंजोब्राईंस

1
यह सवाल पूछने से अधिक समान है, " रोमन ने हमारे लिए क्या नहीं किया है?", क्योंकि यह अंतिम कीवर्ड क्या नहीं करता है पर एक आलोचनात्मक है।
NobleUplift

"क्या आप यह सुझाव दे रहे हैं कि डेवलपर्स का एक महत्वपूर्ण निकाय यह सुझाव दे रहा है कि फ़ाइनल का वास्तव में होने की तुलना में अधिक प्रभाव है?" मेरे लिए यह मुख्य समस्या है: मुझे डेवलपर्स के एक महत्वपूर्ण अनुपात पर दृढ़ता से संदेह है, क्योंकि यह लगता है कि यह कॉल करने वाले के पारित होने की अपरिवर्तनीयता को लागू नहीं करता है। बेशक, एक तब इस बहस में शामिल हो जाता है कि क्या कोडिंग मानकों को 'वैचारिक गलतफहमी से बचाव करना चाहिए' (जो एक 'सक्षम' डेवलपर को पता होना चाहिए) या नहीं (और फिर यह एक आउट-ऑफ-स्कोप राय की ओर जाता है -प्रश्न सवाल)!
स्टुअर्ट रॉसिटर

1
@SarthakMittal: मान की प्रतिलिपि तब तक नहीं बनाई जाएगी जब तक आप वास्तव में इसका उपयोग नहीं करते हैं, यदि आप यही सोच रहे हैं।
जॉन स्कीट

76

मुझे एक मामले में जहां आप के बारे में थोड़ा समझाता हूँ है अंतिम उपयोग करने के लिए है, जो जॉन पहले ही उल्लेख किया:

यदि आप अपनी विधि में एक अनाम आंतरिक वर्ग बनाते हैं और उस वर्ग के अंदर एक स्थानीय चर (जैसे विधि पैरामीटर) का उपयोग करते हैं, तो संकलक आपको पैरामीटर को अंतिम बनाने के लिए मजबूर करता है:

public Iterator<Integer> createIntegerIterator(final int from, final int to)
{
    return new Iterator<Integer>(){
        int index = from;
        public Integer next()
        {
            return index++;
        }
        public boolean hasNext()
        {
            return index <= to;
        }
        // remove method omitted
    };
}

यहां fromऔर toमापदंडों को अंतिम रूप देने की आवश्यकता है ताकि उन्हें अनाम वर्ग के अंदर उपयोग किया जा सके।

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

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


1
आपने मुझे "लेकिन यदि वे अंतिम नहीं हैं ..." से खो दिया है, तो क्या आप इसे फिर से लागू करने का प्रयास कर सकते हैं, शायद मेरे पास पर्याप्त कोफ़ी नहीं थी।
हफीज

1
आपके पास एक स्थानीय चर है - सवाल यह है कि क्या होता है यदि आप विधि के अंदर anon वर्ग उदाहरण का उपयोग करते हैं और यह से मान बदलता है - लोगों को विधि में दृश्यमान परिवर्तन की उम्मीद होगी, क्योंकि वे केवल एक चर देखते हैं। इस भ्रम से बचने के लिए, यह अंतिम होना चाहिए।
माइकल बॉर्गवर्ड

यह एक प्रतिलिपि नहीं बनाता है, यह बस एक संदर्भ है जो भी वस्तु को संदर्भित किया गया था।
vickirk

1
@vickirk: यकीन है कि यह एक प्रति बनाता है - संदर्भ प्रकारों के मामले में।
माइकल बोर्गवर्ड

Btw यह मानते हुए कि हमारे पास उन चरों को संदर्भित करने वाली अनाम कक्षाएं नहीं हैं, क्या आप जानते हैं कि यदि finalहॉटस्पॉट की नजर में कोई फंक्शन पैरामीटर और नॉन-फाइनल फंक्शन पैरामीटर के बीच कोई अंतर है ?
पचेरियर

25

मैं मापदंडों पर हर समय अंतिम उपयोग करता हूं।

क्या यह इतना जोड़ता है? ज़रुरी नहीं।

क्या मैं इसे बंद कर दूंगा? नहीं।

कारण: मुझे 3 बग मिले जहां लोगों ने मैला कोड लिखा था और एक्सेसर्स में एक सदस्य चर सेट करने में विफल रहे। सभी कीड़े खोजने में मुश्किल साबित हुए।

मैं इसे जावा के भविष्य के संस्करण में डिफ़ॉल्ट बनाना चाहता हूं। पास / वैल्यू / रेफरेंस चीज़ों से यात्रा करने वालों में जूनियर प्रोग्रामर की भारी कमी होती है।

एक और बात .. मेरे तरीकों में मापदंडों की कम संख्या होती है इसलिए एक विधि घोषणा पर अतिरिक्त पाठ एक मुद्दा नहीं है।


4
मैं इसके बारे में भी सुझाव देने वाला था, कि भविष्य के संस्करणों में डिफ़ॉल्ट अंतिम होगा और आपको "म्यूटेबल" या बेहतर कीवर्ड निर्दिष्ट करना होगा, जिसकी कल्पना की गई है। यहाँ इस बारे में एक अच्छा लेख है: lpar.ath0.com/2008/08/26/java-annoyance-final-parameters
Jeff Axelrod

यह एक लंबा समय रहा है, लेकिन क्या आप पकड़े गए बग का उदाहरण प्रदान कर सकते हैं?
user949300

जवाब के लिए सबसे अधिक मतदान देखें। इसका एक बहुत अच्छा उदाहरण है जहां सदस्य चर सेट नहीं किया गया है और इसके बजाय पैरामीटर उत्परिवर्तित है।
Fortyrunner

18

विधि पैरामीटर में अंतिम का उपयोग करने से कॉल करने वाले पक्ष के तर्क का कोई लेना-देना नहीं है। इसका मतलब केवल उस तरीके को बदलना है जो उस पद्धति के अंदर नहीं बदल रहा है। जैसा कि मैं एक अधिक कार्यात्मक प्रोग्रामिंग शैली को अपनाने की कोशिश करता हूं, मैं उस तरह के मूल्य को देखता हूं।


2
वास्तव में, यह फ़ंक्शन इंटरफ़ेस का हिस्सा नहीं है, केवल कार्यान्वयन है। यह भ्रामक / अमूर्त विधि घोषणाओं में मापदंडों पर जावा अनुमति देता है (लेकिन dirsregards) final
बेनी चेर्नियाव्स्की-पास्किन

8

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

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

मैं निश्चित रूप से सी / सी ++ कॉन्स्टेबल की तरह कुछ मजबूत करना चाहूंगा।


सुनिश्चित नहीं है कि आईडीई और उपकरण संदर्भ ओपी पोस्टिंग या विषय पर लागू होते हैं। अर्थात् "अंतिम" एक संकलन-समय की जाँच है कि संदर्भ को परिवर्तित / मग नहीं किया गया है। इसके अलावा, वास्तव में ऐसी चीजों को लागू करने के लिए अंतिम संदर्भों के बच्चे के सदस्यों को कोई सुरक्षा नहीं होने के बारे में उत्तर देखें। उदाहरण के लिए एक एपीआई का निर्माण करते समय, एक आईडीई या उपकरण का उपयोग करके बाहरी दलों को ऐसे कोड का उपयोग / विस्तार करने में मदद नहीं मिलेगी।
डेरेल टेग

4

चूंकि जावा तर्कों की प्रतियों को पास करता है, मुझे लगता है कि इसकी प्रासंगिकता finalसीमित है। मुझे लगता है कि आदत सी ++ युग से आती है जहां आप संदर्भ सामग्री को ए करने से बदलने से रोक सकते हैं const char const *। मुझे लगता है कि इस तरह का सामान आपको विश्वास दिलाता है कि डेवलपर स्वाभाविक रूप से च *** के रूप में बेवकूफ है और उसे हर प्रकार के चरित्र के खिलाफ संरक्षित होने की आवश्यकता है। सभी विनम्रता में मैं कह सकता हूं, मैं finalतब भी बहुत कम कीड़े लिखता हूं, जब तक कि मैं चूक नहीं करता (जब तक कि मैं किसी को मेरे तरीकों और कक्षाओं को ओवरराइड नहीं करना चाहता)। शायद मैं सिर्फ एक पुराने स्कूल का देव हूं।


1

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

private String getString(String A, int i, String B, String C)
{
    if (i > 0)
        A += B;

    if (i > 100)
        A += C;

    return A;
}

शैतान के वकील की भूमिका निभाना, ऐसा करने में क्या गलत है?


एक IDE को रनटाइम JVM से अलग करने के लिए आभारी रहें। जब भी कोई IDE अप्रासंगिक होता है, जब संकलित बाइट कोड सर्वर पर चलता है, जब तक IDE ने कोड चर सदस्य मगिंग से बचाने के लिए कोड को दोष के रूप में कोड में दोष नहीं दिया, जब यह इरादा था कि एक चर को फिर से डिज़ाइन नहीं किया जाना चाहिए, लेकिन गलती से - इसलिए इसका उद्देश्य अंतिम कीवर्ड।
डेरेल टेग्यूग

1

संक्षिप्त उत्तर: finalएक छोटे से मदद करता है, लेकिन ... इसके बजाय ग्राहक पक्ष पर रक्षात्मक प्रोग्रामिंग का उपयोग करें।

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


2
"अंतिम के साथ समस्या यह है कि यह केवल संदर्भ को अपरिवर्तित करता है" - असत्य, जावा स्वयं को रोकता है। एक विधि में पारित चर उस विधि द्वारा अपना संदर्भ बदल नहीं सकता है।
मैडब्रेक्स

कृपया पोस्ट करने से पहले शोध करें ... stackoverflow.com/questions/40480/…
Darrell Teague

सीधे शब्दों में, अगर यह सच था कि संदर्भों को बदला नहीं जा सकता है, तो रक्षात्मक-नकल, अपरिवर्तनीयता, अंतिम कीवर्ड की कोई आवश्यकता नहीं होगी, आदि
डेरेल टीग्रे

या तो तुम मुझे गलत समझ रहे हो, या तुम गलत हो। यदि मैं एक विधि के लिए एक वस्तु संदर्भ पास करता हूं, और वह विधि इसे फिर से असाइन करती है, तो मूल संदर्भ तब (मेरे) कॉलर के लिए बरकरार रहता है जब विधि अपना निष्पादन पूरा करती है। जावा सख्ती से पास-वैल्यू है। और तुम सच में मैं कोई शोध किया है मुखर करने के लिए अनुमान लगा रहे हैं।
मैडब्रेक्स

Downvoting क्योंकि op ने पूछा कि अंतिम का उपयोग क्यों करें, और आपने सिर्फ एक गलत कारण दिया।
मैडब्रेक्स

0

अंतिम रूप से पैरामीटर घोषणाओं को जोड़ने का एक अतिरिक्त कारण यह है कि यह उन चर की पहचान करने में मदद करता है जिन्हें "एक्स्ट्रेक्ट मेथड" रीफैक्टरिंग के हिस्से के रूप में नाम बदलने की आवश्यकता होती है। मैंने पाया है कि एक बड़ी विधि को फिर से शुरू करने से पहले प्रत्येक पैरामीटर को अंतिम रूप देने से मुझे जल्दी से पता चलता है कि क्या कोई समस्या है जो मुझे जारी रखने से पहले पता करने की आवश्यकता है।

हालांकि, मैं आम तौर पर उन्हें रिफैक्टरिंग के अंत में बहुत कमज़ोर के रूप में निकालता हूं।


-1

मिशेल की पोस्ट का अनुसरण करें। मैंने इसे समझाने के लिए खुद अपना एक और उदाहरण बनाया। मुझे उम्मीद है कि यह मदद कर सकता है।

public static void main(String[] args){
    MyParam myParam = thisIsWhy(new MyObj());
    myParam.setArgNewName();

    System.out.println(myParam.showObjName());
}

public static MyParam thisIsWhy(final MyObj obj){
    MyParam myParam = new MyParam() {
        @Override
        public void setArgNewName() {
            obj.name = "afterSet";
        }

        @Override
        public String showObjName(){
            return obj.name;
        }
    };

    return myParam;
}

public static class MyObj{
    String name = "beforeSet";
    public MyObj() {
    }
}

public abstract static class MyParam{
    public abstract void setArgNewName();
    public abstract String showObjName();
}

इसके बाद के संस्करण कोड, विधि में से thisIsWhy () , हम वास्तव में आवंटित नहीं किया, [तर्क MyObj obj] एक करने के लिए वास्तविक संदर्भ MyParam में। इसके बजाय, हम सिर्फ MyParam के अंदर विधि में [तर्क MyObj obj] का उपयोग करते हैं।

लेकिन जब हम इस विधि को समाप्त कर लेते हैं तब () , क्या तर्क (वस्तु) MyObj अभी भी मौजूद है?

ऐसा लगता है, क्योंकि हम मुख्य में देख सकते हैं हम अभी भी विधि showObjName () कहते हैं और इसे obj तक पहुंचने की आवश्यकता है । MyParam अभी भी उपयोग करेगा / विधि तर्क तक पहुँचता है यहाँ तक कि विधि पहले ही वापस आ गई थी!

कैसे जावा वास्तव में प्राप्त इस उत्पन्न करने के लिए एक प्रति भी की एक छिपा हुआ संदर्भ है तर्क MyObj obj MyParam ऑब्जेक्ट के अंदर (लेकिन यह है कि हम इसे नहीं देख सकते MyParam में एक औपचारिक क्षेत्र नहीं है तो)

जैसा कि हम "showObjName" कहते हैं, यह संबंधित मूल्य प्राप्त करने के लिए उस संदर्भ का उपयोग करेगा।

लेकिन अगर हमने तर्क को अंतिम रूप नहीं दिया, तो ऐसी स्थिति पैदा होती है कि हम एक नई स्मृति (ऑब्जेक्ट) को तर्क में बदल सकते हैं MyObj obj

तकनीकी रूप से वहाँ कोई संघर्ष नहीं है! यदि हमें ऐसा करने की अनुमति दी जाती है, तो नीचे की स्थिति होगी:

  1. अब हमारे पास एक छिपा हुआ [MyObj obj] बिंदु है [एक मेमोरी में ढेर] अब MyParam ऑब्जेक्ट में रहते हैं।
  2. हमारे पास एक और [MyObj obj] भी है, जो तर्क में एक [स्मृति बी में ढेर] है, अब इस विधि में रहते हैं।

कोई टकराव नहीं, लेकिन "कन्फ्यूजिंग !!" क्योंकि वे सभी एक ही "संदर्भ नाम" का उपयोग कर रहे हैं जो कि "ओब्ज" है

इससे बचने के लिए, प्रोग्रामर को "गलती से ग्रस्त" कोड से बचने के लिए इसे "अंतिम" के रूप में सेट करें।

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