متى يجب استخدام Final لمعلمات الطريقة والمتغيرات المحلية؟

StackOverflow https://stackoverflow.com/questions/154314

  •  03-07-2019
  •  | 
  •  

سؤال

لقد وجدت اثنين من المراجع (على سبيل المثال) التي تقترح استخدام final قدر الإمكان وأنا أتساءل عن مدى أهمية ذلك.هذا بشكل أساسي في سياق معلمات الطريقة والمتغيرات المحلية، وليس الطرق أو الفئات النهائية.بالنسبة للثوابت، فمن المنطقي.

من ناحية، يمكن للمترجم إجراء بعض التحسينات ويجعل نية المبرمج أكثر وضوحًا.من ناحية أخرى، فإنه يضيف الإسهاب والتحسينات قد تكون تافهة.

هل هو شيء يجب أن أبذل جهدًا لتذكره؟

هل كانت مفيدة؟

المحلول

تستحوذ على أكثر من:

  • الحقول النهائية - وضع علامة على الحقول كحقول نهائية يفرض تعيينها بنهاية الإنشاء، مما يجعل مرجع الحقل هذا غير قابل للتغيير.يتيح ذلك النشر الآمن للحقول ويمكن أن يتجنب الحاجة إلى المزامنة في عمليات القراءة اللاحقة.(لاحظ أنه بالنسبة لمرجع كائن، فإن مرجع الحقل فقط هو غير القابل للتغيير - فالأشياء التي يشير إليها مرجع الكائن لا يزال من الممكن أن تتغير مما يؤثر على عدم قابلية التغيير.)
  • الحقول الثابتة النهائية - على الرغم من أنني أستخدم التعدادات الآن في العديد من الحالات التي اعتدت فيها استخدام الحقول النهائية الثابتة.

فكر ولكن استخدم بحكمة:

  • الفئات النهائية - تصميم الإطار/واجهة برمجة التطبيقات (API) هو الحالة الوحيدة التي أعتبرها كذلك.
  • الطرق النهائية - في الأساس نفس الطبقات النهائية.إذا كنت تستخدم أنماط أسلوب القالب مثل المجنونة ووضع علامة نهائية على الأشياء، فمن المحتمل أنك تعتمد كثيرًا على الميراث ولا تعتمد بشكل كافٍ على التفويض.

تجاهل ما لم تشعر بالشرج:

  • معلمات الطريقة والمتغيرات المحلية - نادرًا ما أفعل هذا إلى حد كبير لأنني كسول وأجد أنه يفسد الكود.سأعترف تمامًا بأن تحديد المعلمات والمتغيرات المحلية التي لن أقوم بتعديلها هو "الأصح".أتمنى أن يكون الافتراضي.لكن الأمر ليس كذلك وأجد صعوبة في فهم الكود مع انتهاء الاختبارات النهائية في كل مكان.إذا كنت في رمز شخص آخر، فلن أقوم بسحبه ولكن إذا كنت أكتب رمزًا جديدًا فلن أضعه فيه.الاستثناء الوحيد هو الحالة التي يتعين عليك فيها وضع علامة نهائية على شيء ما حتى تتمكن من الوصول إليه من داخل فئة داخلية مجهولة.

نصائح أخرى

<اقتباس فقرة>   

هل هناك شيء أود أن تبذل جهدا لنتذكر أن تفعل؟

لا، إذا كنت تستخدم الكسوف، لأنه يمكنك تكوين حفظ العمل لإضافة هذه <م> الأخيرة معدلات تلقائيا بالنسبة لك. ثم تحصل على فوائد لأقل جهد.

وفوائد لمرة وتطوير "نهائيا" على الأقل بنفس أهمية الفوائد وقت التشغيل. ويحكي المحررين في المستقبل من قانون شيئا عن نواياكم.

ووسم فئة "النهائية" يشير إلى أنك قد لا بذلت جهدا خلال تصميم أو تنفيذ الطبقة للتعامل مع تمديد بأمان. إذا كان يمكن للقراء إجراء تغييرات على الطبقة، وتريد إزالة التعديل "النهائي"، فإنها يمكن أن تفعل ذلك على مسؤوليتهم الخاصة. والامر متروك لهم للتأكد من أن الطبقة التعامل مع التمديد أيضا.

ووسم متغير "نهائيا" (وتعيين في منشئ) مفيد مع حقن التبعية. فهذا دليل على "متعاون" طبيعة المتغير.

ووسم طريقة "نهائية" غير مفيدة في فئات مجردة. كما يحدد بوضوح أين هي نقاط التمديد.

ولقد وجدت علامات معلمات الأسلوب والسكان المحليين كما final يكون مفيدا كأداة مساعدة في إعادة بيع ديون عندما الأسلوب في السؤال هو فوضى غير مفهومة عدة صفحات طويلة. final رش تحرري، انظر ماذا "لا يمكن تعيين لالنهائية متغير" أخطاء المترجم (أو IDE الخاص بك) تبرزها، وكنت للتو قد تكتشف لماذا متغير يسمى "بيانات" ينتهي باطلة حتى ولو عدة (من التاريخ) تعليقات أقسم أن لا يمكن أن يحدث.

وبعد ذلك يمكن إصلاح بعض الأخطاء عن طريق استبدال المتغيرات استخدامها مع المتغيرات الجديدة التي أعلنت أقرب إلى نقطة الاستخدام. ثم تجد يمكنك لف أجزاء كاملة من الأسلوب في الفحص الأقواس، وفجأة كنت واحدا بضغطة IDE بعيدا عن "استخراج أسلوب" والوحش فقط حصلت على أكثر مفهومة.

إذا الأسلوب الخاص بك هو لا بالفعل حطام غير قابل للدعم، واعتقد قد يكون هناك قيمة في صنع الاشياء النهائية لثني الناس عن تحويله إلى حطام قال. ولكن اذا كان وسيلة قصيرة (انظر: لا غير قابل للدعم) ثم هل خطر إضافة الكثير من الإسهاب. على وجه الخصوص، والتوقيعات وظيفة جافا من الصعب بما يكفي لتناسب 80 حرفا كما هو دون إضافة ستة آخرين في حجة!

وأنا استخدم final كل الوقت لجعل جافا المزيد من التعبير القائمة. انظر شروط جاوة (if,else,switch) لا أساس التعبير التي أشرت يكره دائما خاصة إذا كان لديك تستخدم لبرمجة وظيفية (أي ML، سكالا أو اللثغة).

وهكذا عليك أن تحاول دائما (IMHO) استخدام المتغيرات النهائية عند استخدام الظروف.

واسمحوا لي أن أقدم لكم مثالا:

    final String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            break;
        case JOB_POSTING_IMPORT:
            name = "Blah";
            break;
        default:
            throw new IllegalStateException();
    }

والآن إذا أضيف بيان case آخر وعدم تعيين name ستفشل المترجم. ستفشل المترجم أيضا إذا لم يكن لكسر في كل حالة (أي قمت بتعيين متغير). هذا يسمح لك لجعل جافا تشبه الى حد بعيد تعبيرات let اللثغة ويجعل من ذلك التعليمات البرمجية ليست بادئة نطاق واسع (بسبب المتغيرات الاستطلاعية المعجمية).

وكما لاحظتRecurse (ولكن على ما يبدو -1 لي) يمكنك القيام السابقة مع عدم تمكين String name final الحصول على الخطأ مترجم (التي لم أكن وقال لا يمكن) ولكن هل يمكن بسهولة جعل الخطأ مترجم تزول وضع الاسم بعد بيان التحول الذي يلقي بعيدا دلالات التعبير أو ما هو أسوأ نسيان break التي لا يمكن أن يسبب خطأ (رغم ما يقولهRecurse) دون استخدام final:

    String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            //break; whoops forgot break.. 
            //this will cause a compile error for final ;P @Recurse
        case JOB_POSTING_IMPORT:
            name = "Blah";
            break;
    }
    // code, code, code
    // Below is not possible with final
    name = "Whoops bug";

ونظرا للاسم الإعداد علة (إلى جانب نسيان break والتي أيضا علة أخرى) يمكنني الآن دون قصد القيام بذلك:

    String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            break;
        //should have handled all the cases for pluginType
    }
    // code, code, code
    // Below is not possible with final
    name = "Whoops bug";

والمتغير النهائي قوات تقييم واحد لما يجب أن يكون اسم. على غرار الطريقة وظيفة له قيمة الإرجاع يجب دوما بإرجاع قيمة (استثناءات تجاهل) مفتاح كتلة اسم سوف تضطر إلى حل اسم وبالتالي منضمة إلى أن كتلة التحول الذي يجعل قطع إعادة بيع ديون من التعليمات البرمجية أسهل (أي Eclipe ريفاكتور: استخراج طريقة) .

ما سبق في لغة كامل الموضوعية:

type plugin = CandidateExport | JobPostingImport

let p = CandidateExport

let name = match p with
    | CandidateExport -> "Candidate Stuff"
    | JobPostingImport -> "Blah" ;;

ووmatch ... with ... يقيم مثل التعبير وظيفة أي. لاحظ كيف يبدو بيان التبديل لدينا.

وهنا مثال في برنامج (مضرب أو الدجاج):

(define name 
    (match b
      ['CandidateExport "Candidate Stuff"]
      ['JobPostingImport "Blah"]))

حسنا، هذا كله يتوقف على طريقتك ... إذا كنت ترغب في رؤية نهائي عند لن يتم تعديل متغير، ثم استخدامه. إذا كنت لا تحب رؤيتها ... ثم ترك ذلك.

وأنا شخصيا مثل الإسهاب أقل قدر ممكن، لذلك أنا أميل إلى تجنب استخدام الكلمات الرئيسية الإضافية التي ليست ضرورية حقا.

وأنا أفضل اللغات الديناميكية على الرغم من ذلك فإنه من المحتمل لم يكن مفاجئا أود أن تجنب الإسهاب.

وهكذا، وأود أن أقول فقط اختيار الاتجاه الذي يميل نحو واذهبوا معه (مهما كانت الحالة، حاول أن تكون متسقة).


وكملاحظة جانبية، لقد عملت على المشاريع التي على حد سواء استخدام وعدم استخدام مثل هذا النمط، ولقد رأيت لا فرق في مقدار الخلل أو الأخطاء ... وأنا لا أعتقد أنه هو النمط التي من شأنها تحسين بشكل كبير العد علة أو أي شيء، ولكن مرة أخرى أنها الأسلوب، وإذا أردت التعبير عن النية أنك لن تعديله، ثم المضي قدما واستخدامها.

ومن المفيد في المعلمات لتجنب تغيير قيمة المعلمة عن طريق الصدفة وإدخال علة خفية. أنا استخدامها لتجاهل هذه التوصية ولكن بعد أن أمضى حوالي 4 ساعات. في طريقة الرهيبة (مع مئات من خطوط رمز وفوريس متعددة، المؤسسة الدولية للعلوم المتداخلة وكل نوع من الممارسات السيئة) أنصح لك أن تفعل ذلك.

 public int processSomethingCritical( final int x, final int y ){
 // hundreds of lines here 
     // for loop here...
         int x2 = 0;
        x++; // bug aarrgg...
 // hundreds of lines there
 // if( x == 0 ) { ...

 }

وبطبيعة الحال في عالم مثالي وهذا لن يحدث، ولكن .. حسنا .. في بعض الأحيان لديك لدعم كود الآخرين. :(

إذا كنت تكتب تطبيق أن شخصا ما سوف يكون لقراءة الشفرة بعد، ويقول، 1 سنة، ثم نعم، استخدم المباراة النهائية في المتغير الذي لا ينبغي تعديلها في كل وقت. وعند القيام بذلك، سوف التعليمات البرمجية الخاصة بك أن تكون أكثر "توثيق الذاتي" وأنت أيضا تقليل فرصة للمطورين الآخرين أن تفعل أشياء سخيفة مثل استخدام ثابت المحلي كمتغير مؤقت محلي.

إذا كنت تكتب بعض رمز بخس، ثم، قام خلاله، لا تهتم لتحديد جميع ثابت وجعلها نهائي.

وسأستخدم النهائي بقدر ما أستطيع. وهذا من شأنه العلم إذا قصد تغيير المجال. أنا أيضا تعيين المعلمات الأسلوب إلى المباراة النهائية. القيام بذلك لقد اشتعلت العديد من الأخطاء من التعليمات البرمجية لقد استولت عندما يحاولون "مجموعة" معلمة نسيان جافا يمر من حيث القيمة.

ليس من الواضح من السؤال ما إذا كان هذا واضحًا، ولكن جعل معلمة الطريقة نهائية يؤثر فقط على نص الطريقة.نعم هو كذلك لا نقل أي معلومات مثيرة للاهتمام حول نوايا الطريقة إلى المستدعي.لا يزال من الممكن تغيير الكائن الذي يتم تمريره داخل الطريقة (النهائيات ليست ثوابت)، ويكون نطاق المتغير ضمن الطريقة.

للإجابة على سؤالك الدقيق، لن أزعج نفسي بجعل مثيل أو متغير محلي (بما في ذلك معلمات الطريقة) نهائيًا ما لم يتطلب الكود ذلك (على سبيل المثال.يتم الرجوع إلى المتغير من فئة داخلية)، أو لتوضيح بعض المنطق المعقد حقًا.

على سبيل المثال، المتغيرات، سأجعلها نهائية إذا كانت ثوابت منطقية.

هناك العديد من الاستخدامات للمتغير final.هنا ليست سوى عدد قليل

الثوابت النهائية

 public static class CircleToolsBetter {
     public final static double PI = 3.141;
        public double getCircleArea(final double radius) {
          return (Math.pow(radius, 2) * PI);
        }
    }

يمكن استخدام هذا بعد ذلك لأجزاء أخرى من أكوادك، أو الوصول إليه بواسطة فئات أخرى، وبهذه الطريقة إذا قمت بتغيير القيمة، فلن تضطر إلى تغييرها واحدة تلو الأخرى.

المتغيرات النهائية

public static String someMethod(final String environmentKey) {
    final String key = "env." + environmentKey;
    System.out.println("Key is: " + key);
    return (System.getProperty(key));

  }

}

في هذه الفئة، يمكنك إنشاء متغير نهائي محدد النطاق يضيف بادئة إلى المعلمة EnvironmentKey.في هذه الحالة، يكون المتغير النهائي نهائيًا فقط ضمن نطاق التنفيذ، والذي يختلف عند كل تنفيذ للطريقة.في كل مرة يتم إدخال الطريقة، يتم إعادة بناء الشكل النهائي.بمجرد إنشائه، لا يمكن تغييره أثناء نطاق تنفيذ الطريقة.يتيح لك هذا إصلاح متغير في طريقة ما طوال مدة الطريقة.انظر أدناه:

public class FinalVariables {


  public final static void main(final String[] args) {
    System.out.println("Note how the key variable is changed.");
    someMethod("JAVA_HOME");
    someMethod("ANT_HOME");
  }
}

الثوابت النهائية

public double equation2Better(final double inputValue) {
    final double K = 1.414;
    final double X = 45.0;

double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M);
double powInputValue = 0;         
if (result > 360) {
  powInputValue = X * Math.sin(result); 
} else {
  inputValue = K * Math.sin(result);   // <= Compiler error   
}

تكون هذه مفيدة بشكل خاص عندما يكون لديك سطور طويلة جدًا من الأكواد، وسوف تولد خطأ في المترجم حتى لا تتعرض لخطأ منطقي/عملي عندما يقوم شخص ما عن طريق الخطأ بتغيير المتغيرات التي لا ينبغي تغييرها.

المجموعات النهائية

هناك حالة مختلفة عندما نتحدث عن المجموعات، فأنت بحاجة إلى تعيينها كمجموعة غير قابلة للتعديل.

 public final static Set VALID_COLORS; 
    static {
      Set temp = new HashSet( );
      temp.add(Color.red);
      temp.add(Color.orange);
      temp.add(Color.yellow);
      temp.add(Color.green);
      temp.add(Color.blue);
      temp.add(Color.decode("#4B0082")); // indigo
      temp.add(Color.decode("#8A2BE2")); // violet
      VALID_COLORS = Collections.unmodifiableSet(temp);
    }

وإلا، إذا لم تقم بتعيينه على أنه غير قابل للتعديل:

Set colors = Rainbow.VALID_COLORS;
colors.add(Color.black); // <= logic error but allowed by compiler

الفصول النهائية و الطرق النهائية لا يمكن تمديدها أو الكتابة فوقها على التوالي.

تحرير: لمعالجة مشكلة الفصل النهائي فيما يتعلق بالتغليف:

هناك طريقتان لجعل الفصل الدراسي نهائيًا.الأول هو استخدام الكلمة الأساسية Final في إعلان الفصل:

public final class SomeClass {
  //  . . . Class contents
}

الطريقة الثانية لجعل الفصل نهائيًا هي الإعلان عن أن جميع منشئيه خاصون:

public class SomeClass {
  public final static SOME_INSTANCE = new SomeClass(5);
  private SomeClass(final int value) {
  }

وضع علامة نهائية عليه يوفر عليك المتاعب إذا اكتشفت أنه نهائي فعليًا، لتوضيح إلقاء نظرة على فئة الاختبار هذه.تبدو علنية للوهلة الأولى.

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}

لسوء الحظ، بما أن المنشئ الوحيد للفئة خاص، فمن المستحيل توسيع هذه الفئة.في حالة الفصل الاختباري، لا يوجد سبب يجعل الفصل نهائيًا.تعد فئة الاختبار مثالًا جيدًا لكيفية تسبب الفئات النهائية الضمنية في حدوث مشكلات.

لذا يجب عليك وضع علامة نهائية عندما تجعل الفصل ضمنيًا نهائيًا عن طريق جعل مُنشئه خاصًا.

وإلى حد ما من مفاضلة كما ذكرتم، ولكن يفضل استخدام صريحة من شيء أكثر من استخدام الضمني. هذا وسوف تساعد على إزالة بعض الغموض لمشرفون في المستقبل من التعليمات البرمجية - حتى لو كان مجرد لك.

إذا كان لديك الداخلية (مجهول) الطبقات، وتحتاج طريقة للوصول إلى متغير من طريقة تحتوي على، تحتاج إلى أن يكون هذا المتغير على أنه نهائي.

وعدا ذلك، ما قلته هو الصحيح.

يستخدم final الكلمة الأساسية لمتغير إذا كنت تقوم بإنشاء هذا المتغير كـ immutable

من خلال الإعلان عن المتغير كنهائي، فإنه يساعد المطورين على استبعاد مشكلات التعديل المحتملة للمتغيرات في بيئة متعددة الخيوط.

مع إصدار Java 8، لدينا مفهوم آخر يسمى "effectively final variable". يمكن للمتغير غير النهائي أن يتحول إلى متغير نهائي.

يجب أن تكون المتغيرات المحلية المشار إليها من تعبير لامدا نهائية أو نهائية بشكل فعال

يعتبر متغير نهائي فعال إذا لم يتم تعديله بعد التهيئة في الكتلة المحلية.هذا يعني أنه يمكنك الآن استخدام المتغير المحلي بدون الكلمة الأساسية النهائية داخل فئة مجهولة أو تعبير لامدا، بشرط أن تكون نهائية بشكل فعال.

حتى Java 7، لا يمكنك استخدام متغير محلي غير نهائي داخل فئة مجهولة، ولكن من Java 8 يمكنك ذلك

الق نظرة على هذا شرط

والجواب بسيط جدا لدينا 3 حالات مع Final مع المتغيرات، نهائي مع أساليب && النهائي مع الطبقات ..

و1.Final مع متغير: ش 'ر تعيين هذا المتغير أكثر من مرة واحدة ..

و2.Final مع طرق: ش 'ر تجاوز هذه الطريقة ..

و3.Final مع فئات هي: ش 'ر تمديد أي فئة النهائية

وأولا وقبل كل شيء، يتم استخدام الكلمة الأخيرة لجعل ثابت متغير. يعني ثابت لا يتغير. على سبيل المثال:

final int CM_PER_INCH = 2.54;

وأنت ستعلن المباراة النهائية متغير بسبب عدم تغيير سنتيمتر واحد لكل بوصة.

إذا كنت في محاولة لتجاوز القيمة النهائية، المتغير هو ما أعلن لأول مرة. على سبيل المثال:

final String helloworld = "Hello World";
helloworld = "A String"; //helloworld still equals "Hello World"

وهناك خطأ ترجمة هذا شيء مثل:

local variable is accessed from inner class, must be declared final

إذا المتغير الخاص بك لا يمكن الإعلان النهائي أو إذا كنت لا تريد أن تعلن نهائي حاول هذا:

final String[] helloworld = new String[1];
helloworld[0] = "Hello World!";
System.out.println(helloworld[0]);
helloworld[0] = "A String";
System.out.println(helloworld[0]);

وهذه الإرادة الطباعة:

Hello World!
A String
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top