JavaRush /مدونة جافا /Random-AR /الطبقات الداخلية بالطريقة المحلية

الطبقات الداخلية بالطريقة المحلية

نشرت في المجموعة
مرحبًا! دعونا نتحدث عن نوع آخر من فئة متداخلة. وهي حول الطبقات المحلية (طريقة الطبقات الداخلية المحلية). أول شيء عليك أن تتذكره قبل الدراسة هو مكانها في بنية الفصول المتداخلة. الطبقات الداخلية بالطريقة المحلية - 2بناءً على الرسم البياني الخاص بنا، يمكننا أن نفهم أن الفئات المحلية هي نوع فرعي من الفئات الداخلية، والتي تحدثنا عنها بالتفصيل في إحدى المواد السابقة . ومع ذلك، فإن الطبقات المحلية لديها عدد من الميزات والاختلافات الهامة من الطبقات الداخلية. المفتاح موجود في إعلانهم: يتم الإعلان عن فئة محلية فقط في كتلة التعليمات البرمجية. في أغلب الأحيان - داخل طريقة ما لفئة خارجية. على سبيل المثال، قد يبدو الأمر كما يلي:
public class PhoneNumberValidator {

   public void validatePhoneNumber(String number) {

        class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       //...code валидации номера
   }
}
مهم!لن يتم تجميع هذا الكود عند لصقه في IDEA إذا كان لديك Java 7. سنتحدث عن أسباب ذلك في نهاية المحاضرة. باختصار، يعتمد عمل الفصول المحلية بشكل كبير على إصدار اللغة. إذا لم يتم تجميع هذا الرمز لك، فيمكنك إما تبديل إصدار اللغة في IDEA إلى Java 8، أو إضافة كلمة finalإلى معلمة الطريقة بحيث تبدو كما يلي: validatePhoneNumber(final String number). بعد هذا كل شيء سوف يعمل. هذا برنامج صغير - مدقق لرقم الهاتف. تأخذ طريقتها validatePhoneNumber()سلسلة كمدخل وتحدد ما إذا كان رقم هاتف. وداخل هذه الطريقة أعلنا عن صنفنا المحلي PhoneNumber. قد يكون لديك سؤال منطقي: لماذا؟ لماذا تعلن فئة داخل الطريقة؟ لماذا لا تستخدم الطبقة الداخلية العادية؟ في الواقع، يمكن للمرء أن يفعل هذا: جعل الفصل PhoneNumberداخليًا. شيء آخر هو أن القرار النهائي يعتمد على هيكل برنامجك والغرض منه. دعونا نتذكر مثالنا من المحاضرة حول الطبقات الداخلية:
public class Bicycle {

   private String model;
   private int mawWeight;

   public Bicycle(String model, int mawWeight) {
       this.model = model;
       this.mawWeight = mawWeight;
   }

   public void start() {
       System.out.println("Go!");
   }

   public class HandleBar {

       public void right() {
           System.out.println("Steering wheel to the right!");
       }

       public void left() {

           System.out.println("Steering wheel to the left!");
       }
   }
}
وفيه صنعنا HandleBar(المقود) فئة داخلية للدراجة. ماهو الفرق؟ بادئ ذي بدء، في استخدام الفصل. الفئة HandleBarمن المثال الثاني هي كيان أكثر تعقيدًا من PhoneNumberالأول. أولاً، y HandleBarلديه طرق عامة rightو left(ليست setter وgetter). ثانيًا، لا يمكننا التنبؤ مسبقًا بالمكان الذي Bicycleقد نحتاج إليه فيه وفئته الخارجية - فقد تكون هذه عشرات الأماكن والأساليب المختلفة، حتى داخل نفس البرنامج. ولكن مع الفصل، PhoneNumberكل شيء أبسط من ذلك بكثير. برنامجنا بسيط جدا. لديها وظيفة واحدة فقط - للتحقق مما إذا كان الرقم هو رقم هاتف. في معظم الحالات، PhoneNumberValidatorلن يكون برنامجنا مجرد برنامج مستقل، بل مجرد جزء من منطق الترخيص للبرنامج الرئيسي. على سبيل المثال، في مواقع الويب المختلفة، عند التسجيل، غالبًا ما يُطلب منك إدخال رقم هاتف. وإذا قمت بكتابة بعض الهراء بدلاً من الأرقام، فسيقوم الموقع بإلقاء خطأ: "هذا ليس رقم هاتف!" لتشغيل مثل هذا الموقع (أو بالأحرى آلية ترخيص المستخدم)، يمكن لمطوريه تضمين نظير لنا في الكود PhoneNumberValidator. بمعنى آخر، لدينا فئة خارجية واحدة بأسلوب واحد سيتم استخدامها في مكان واحد في البرنامج وليس في أي مكان آخر. وإذا حدث ذلك، فلن يتغير شيء فيه: هناك طريقة واحدة تقوم بعملها - هذا كل شيء. في هذه الحالة، نظرا لأن كل منطق العمل يتم جمعه بطريقة واحدة، فسيكون أكثر ملاءمة وصحة لتغليف فئة إضافية هناك. ليس لديها طرقها الخاصة بخلاف getter و setter. نحن في الأساس بحاجة فقط إلى بيانات المنشئ منه. ولا يتم استخدامه في طرق أخرى. ولذلك، لا يوجد سبب لتوسيع المعلومات حول هذا الموضوع خارج الطريقة الوحيدة التي يتم استخدامها فيها. لقد قدمنا ​​مثالاً للإعلان عن فئة محلية في إحدى الطرق، ولكن هذا ليس هو الاحتمال الوحيد. يمكن الإعلان عنها ببساطة في كتلة التعليمات البرمجية:
public class PhoneNumberValidator {

   {
       class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

   }

   public void validatePhoneNumber(String phoneNumber) {


       //...code валидации номера
   }
}
أو حتى في حلقة for!
public class PhoneNumberValidator {


   public void validatePhoneNumber(String phoneNumber) {

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

           class PhoneNumber {

               private String phoneNumber;

               public PhoneNumber(String phoneNumber) {
                   this.phoneNumber = phoneNumber;
               }
           }

           //...Howая-то логика
       }

       //...code валидации номера
   }
}
لكن مثل هذه الحالات نادرة للغاية. في معظم الحالات، سيظل الإعلان يحدث داخل الطريقة. لذلك، تناولنا الإعلان، وتحدثنا أيضًا عن "الفلسفة" :) ما هي الميزات والاختلافات الأخرى التي تتميز بها الطبقات المحلية عن الطبقات الداخلية؟ لا يمكن إنشاء كائن فئة محلي خارج الطريقة أو الكتلة التي تم الإعلان عنها. تخيل أننا بحاجة إلى طريقة generatePhoneNumber()تقوم بإنشاء رقم هاتف عشوائي وإرجاع رقم PhoneNumber. لن نكون قادرين على إنشاء مثل هذه الطريقة في فئة المدقق لدينا في الوضع الحالي:
public class PhoneNumberValidator {

   public void validatePhoneNumber(String number) {

        class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       //...code валидации номера
   }

   //ошибка! компилятор не понимает, что это за класс - PhoneNumber
   public PhoneNumber generatePhoneNumber() {

   }

}
ميزة أخرى مهمة للفئات المحلية هي القدرة على الوصول إلى المتغيرات المحلية ومعلمات الطريقة. في حالة نسيانك، فإن "المحلي" هو متغير تم الإعلان عنه داخل إحدى الطرق. String russianCountryCodeأي أننا إذا قمنا بإنشاء متغير محلي داخل إحدى الطرق لبعض أغراضنا validatePhoneNumber()، فيمكننا الوصول إليه من الفئة المحلية PhoneNumber. ومع ذلك، هناك الكثير من التفاصيل الدقيقة التي تعتمد على إصدار اللغة المستخدمة في البرنامج. في بداية المحاضرة، لاحظنا أن الكود الموجود في أحد الأمثلة قد لا يتم تجميعه في Java 7، هل تتذكر؟ الآن دعونا نلقي نظرة على أسباب ذلك :) في Java 7، يمكن للفئة المحلية الوصول إلى متغير محلي أو معلمة أسلوب فقط إذا تم الإعلان عنها في الطريقة على النحو التالي final:
public void validatePhoneNumber(String number) {

   String russianCountryCode = "+7";

   class PhoneNumber {

       private String phoneNumber;

       //ошибка! параметр метода должен быть объявлен How final!
       public PhoneNumber() {
           this.phoneNumber = number;
       }

       public void printRussianCountryCode() {

           //ошибка! локальная переменная должна быть объявлена How final!
           System.out.println(russianCountryCode);
       }

   }

   //...code валидации номера
}
هنا ألقى المترجم خطأين. ولكن هنا كل شيء في محله:
public void validatePhoneNumber(final String number) {

   final String russianCountryCode = "+7";

    class PhoneNumber {

       private String phoneNumber;


       public PhoneNumber() {
           this.phoneNumber = number;
       }

       public void printRussianCountryCode() {

           System.out.println(russianCountryCode);
       }

    }

   //...code валидации номера
}
الآن أنت تعرف سبب عدم تجميع الكود الموجود في بداية المحاضرة: الفصل المحلي في Java 7 لديه فقط إمكانية الوصول إلى finalمعلمات الطريقة finalوالمتغيرات المحلية. في Java 8، تغير سلوك الفئات المحلية. في هذا الإصدار من اللغة، يمكن للفئة المحلية الوصول ليس فقط إلى finalالمتغيرات والمعلمات -local، ولكن أيضًا إلى effective-final. Effective-finalهو متغير لم تتغير قيمته منذ التهيئة. على سبيل المثال، في Java 8 يمكننا بسهولة عرض متغير على وحدة التحكم russianCountryCode، حتى لو لم يكن كذلك final. الشيء الرئيسي هو أنه لا يغير معناه. في هذا المثال، كل شيء يعمل كما ينبغي:
public void validatePhoneNumber(String number) {

  String russianCountryCode = "+7";

    class PhoneNumber {

       public void printRussianCountryCode() {

           //в Java 7 здесь была бы ошибка
           System.out.println(russianCountryCode);
       }

    }

   //...code валидации номера
}
ولكن إذا قمنا بتغيير قيمة المتغير مباشرة بعد التهيئة، فلن يتم تجميع الكود.
public void validatePhoneNumber(String number) {

  String russianCountryCode = "+7";
  russianCountryCode = "+8";

    class PhoneNumber {

       public void printRussianCountryCode() {

           //error!
           System.out.println(russianCountryCode);
       }

    }

   //...code валидации номера
}
ولكن ليس من قبيل الصدفة أن تكون الطبقة المحلية نوعًا فرعيًا من الطبقة الداخلية! لديهم أيضا نقاط مشتركة. تتمتع الفئة المحلية بإمكانية الوصول إلى جميع الحقول والأساليب (حتى الخاصة) الخاصة بالطبقة الخارجية: الثابتة وغير الثابتة. على سبيل المثال، دعونا نضيف حقلاً ثابتًا إلى فئة التحقق من الصحة String phoneNumberRegex:
public class PhoneNumberValidator {

   private static String phoneNumberRegex = "[^0-9]";

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {

           //......
       }
   }
}
سيتم إجراء التحقق من الصحة باستخدام هذا المتغير الثابت. تتحقق الطريقة مما إذا كانت السلسلة التي تم تمريرها إليها تحتوي على أحرف لا تتطابق مع التعبير العادي " [^0-9]" (أي أن الحرف ليس رقمًا من 0 إلى 9). يمكننا الوصول بسهولة إلى هذا المتغير من الفئة المحلية PhoneNumber. على سبيل المثال، اكتب getter:
public String getPhoneNumberRegex() {

   return phoneNumberRegex;
}
تشبه الفئات المحلية الفئات الداخلية لأنها لا تستطيع تعريف أو الإعلان عن أي أعضاء ثابتين. يمكن للفئات المحلية في الطرق الثابتة أن تشير فقط إلى الأعضاء الثابتين في الفئة المتضمنة. على سبيل المثال، إذا لم تقم بتعريف متغير (حقل) للفئة المتضمنة على أنه ثابت، فسيقوم مترجم Java بإنشاء خطأ: "لا يمكن الرجوع إلى متغير غير ثابت من سياق ثابت." الفئات المحلية ليست ثابتة لأن لديها إمكانية الوصول إلى أعضاء مثيل الكتلة التي تحتوي عليها. ولذلك، لا يمكن أن تحتوي على معظم أنواع الإعلانات الثابتة. لا يمكنك الإعلان عن واجهة داخل كتلة؛ الواجهات ثابتة بطبيعتها. لن يتم تجميع هذا الرمز:
public class PhoneNumberValidator {
   public static void validatePhoneNumber(String number) {
       interface I {}

       class PhoneNumber implements I{
           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }
       }

       //...code валидации номера
   }
}
ولكن إذا تم الإعلان عن واجهة داخل فئة خارجية، PhoneNumberفيمكن للفئة تنفيذها:
public class PhoneNumberValidator {
   interface I {}

   public static void validatePhoneNumber(String number) {

       class PhoneNumber implements I{
           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }
       }

       //...code валидации номера
   }
}
لا يمكن للفئات المحلية أن تعلن عن المُهيئات الثابتة (كتل التهيئة) أو الواجهات. لكن الفئات المحلية يمكن أن تحتوي على أعضاء ثابتين، بشرط أن يكونوا متغيرات ثابتة ( static final). هذا ما هم عليه، الطبقات المحلية! كما ترون، لديهم العديد من الاختلافات عن الطبقات الداخلية. لقد كان علينا أيضًا التعمق في ميزات إصدار اللغة لفهم كيفية عملها :) في المحاضرة التالية، سنتحدث عن الفئات الداخلية المجهولة - المجموعة الأخيرة من الفئات المتداخلة. حظا موفقا في دراستك! :)
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION