أهلاً! دعونا نتحدث عن نوع آخر من الطبقات المتداخلة. أنا أتحدث عن الطبقات المحلية (طريقة الطبقات الداخلية المحلية). قبل الغوص في الأمر، يجب علينا أولًا أن نتذكر مكانهم في بنية الفئات المتداخلة. من الرسم البياني الخاص بنا يمكننا أن نرى أن الطبقات المحلية هي نوع فرعي من الطبقات الداخلية، والتي تحدثنا عنها بالتفصيل في المواد السابقة
. ومع ذلك، تتمتع الطبقات المحلية بعدد من الميزات والاختلافات المهمة عن الطبقات الداخلية العادية. الشيء الرئيسي هو في إعلانهم: يتم الإعلان عن فئة محلية فقط في كتلة من التعليمات البرمجية. في أغلب الأحيان، يكون هذا الإعلان داخل إحدى طرق الطبقة الخارجية. على سبيل المثال، قد يبدو الأمر كما يلي:
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;
}
}
// ...number validation code
}
}
مهم!إذا كان لديك Java 7 مثبتًا، فلن يتم تجميع هذا الرمز عند لصقه في IDEA. سنتحدث عن أسباب ذلك في نهاية الدرس. باختصار، تعتمد كيفية عمل الفصول المحلية بشكل كبير على إصدار اللغة. إذا لم يتم تجميع هذا الرمز لك، فيمكنك إما تبديل إصدار اللغة في IDEA إلى Java 8، أو إضافة الكلمة final
إلى معلمة الطريقة بحيث تبدو كما يلي: validatePhoneNumber(final String number)
. بعد ذلك، كل شيء سوف يعمل. هذا برنامج صغير يقوم بالتحقق من صحة أرقام الهواتف. تأخذ طريقتها validatePhoneNumber()
سلسلة كمدخل وتحدد ما إذا كان رقم هاتف. وداخل هذه الطريقة، أعلنا عن PhoneNumber
صفنا المحلي. قد تسأل بشكل معقول لماذا. لماذا بالضبط نعلن عن فئة داخل الطريقة؟ لماذا لا تستخدم الطبقة الداخلية العادية؟ صحيح أنه كان بإمكاننا أن نجعل من PhoneNumber
الفصل طبقة داخلية. لكن الحل النهائي يعتمد على بنية برنامجك والغرض منه. لنتذكر مثالنا من درس حول الطبقات الداخلية:
public class Bicycle {
private String model;
private int maxWeight;
public Bicycle(String model, int maxWeight) {
this.model = model;
this.maxWeight = maxWeight;
}
public void start() {
System.out.println("Let's go!");
}
public class HandleBar {
public void right() {
System.out.println("Steer right!");
}
public void left() {
System.out.println("Steer left!");
}
}
}
فيه صنعنا HandleBar
فئة داخلية للدراجة. ماهو الفرق؟ بادئ ذي بدء، طريقة استخدام الفصل مختلفة. الفئة HandleBar
في المثال الثاني هي كيان أكثر تعقيدًا من PhoneNumber
الفئة في المثال الأول. أولاً، HandleBar
يحتوي على public right
وطرق left
(هذه ليست أدوات ضبط/حروف). ثانياً، من المستحيل التنبؤ مسبقاً بالمكان الذي قد نحتاج إليه وإلى فئته الخارجية 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) {
// ...number validation 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;
}
}
// ...some logic
}
// ...number validation 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;
}
}
// ...number validation code
}
// Error! The compiler does not recognize the PhoneNumber class
public PhoneNumber generatePhoneNumber() {
}
}
ميزة أخرى مهمة للفئات المحلية هي القدرة على الوصول إلى المتغيرات المحلية ومعلمات الطريقة. في حالة نسيانك، فإن المتغير المعلن داخل إحدى الطرق يُعرف باسم المتغير "المحلي". أي أننا إذا قمنا بإنشاء String usCountryCode
متغير محلي داخل validatePhoneNumber()
الطريقة لسبب ما، فيمكننا الوصول إليه من PhoneNumber
الفئة المحلية. ومع ذلك، هناك الكثير من التفاصيل الدقيقة التي تعتمد على إصدار اللغة المستخدمة في البرنامج. في بداية الدرس، لاحظنا أن كود أحد الأمثلة قد لا يتم تجميعه في Java 7، تذكر؟ الآن دعونا نفكر في أسباب ذلك :) في Java 7، يمكن للفئة المحلية الوصول إلى متغير محلي أو معلمة أسلوب فقط إذا تم الإعلان عنها كما final
في الطريقة:
public void validatePhoneNumber(String number) {
String usCountryCode = "+1";
class PhoneNumber {
private String phoneNumber;
// Error! The method parameter must be declared as final!
public PhoneNumber() {
this.phoneNumber = number;
}
public void printUsCountryCode() {
// Error! The local variable must be declared as final!
System.out.println(usCountryCode);
}
}
// ...number validation code
}
هنا يولد المترجم خطأين. وكل شيء على ما يرام هنا:
public void validatePhoneNumber(final String number) {
final String usCountryCode = "+1";
class PhoneNumber {
private String phoneNumber;
public PhoneNumber() {
this.phoneNumber = number;
}
public void printUsCountryCode() {
System.out.println(usCountryCode);
}
}
// ...number validation code
}
الآن أنت تعرف سبب عدم تجميع التعليمات البرمجية من بداية الدرس: في Java 7، يتمتع الفصل المحلي بإمكانية الوصول فقط إلى final
معلمات الطريقة final
والمتغيرات المحلية. في Java 8، تغير سلوك الفئات المحلية. في هذا الإصدار من اللغة، يتمتع الفصل المحلي بإمكانية الوصول ليس فقط إلى final
المتغيرات والمعلمات المحلية، ولكن أيضًا إلى تلك المتغيرات والمعلمات effective-final
. Effective-final
هو متغير لم تتغير قيمته منذ التهيئة. على سبيل المثال، في Java 8، يمكننا بسهولة عرض المتغير usCountryCode
على وحدة التحكم، حتى لو لم يكن كذلك final
. المهم أن قيمته لا تتغير. في المثال التالي، كل شيء يعمل كما ينبغي:
public void validatePhoneNumber(String number) {
String usCountryCode = "+1";
class PhoneNumber {
public void printUsCountryCode() {
// Java 7 would produce an error here
System.out.println(usCountryCode);
}
}
// ...number validation code
}
ولكن إذا قمنا بتغيير قيمة المتغير مباشرة بعد التهيئة، فلن يتم تجميع الكود.
public void validatePhoneNumber(String number) {
String usCountryCode = "+1";
usCountryCode = "+8";
class PhoneNumber {
public void printUsCountryCode() {
// Error!
System.out.println(usCountryCode);
}
}
// ...number validation 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;
}
}
// ...number validation 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;
}
}
// ...number validation code
}
}
لا يمكن الإعلان عن المُهيئات الثابتة (كتل التهيئة) أو الواجهات في الفئات المحلية. لكن الفئات المحلية يمكن أن تحتوي على أعضاء ثابتين، بشرط أن يكونوا متغيرات ثابتة ( static final
). والآن أنتم تعرفون عن الطبقات المحلية، أيها الناس! كما ترون، لديهم العديد من الاختلافات عن الطبقات الداخلية العادية. لقد كان علينا أيضًا التعمق في ميزات إصدارات معينة من اللغة لفهم كيفية عملها :) في الدرس التالي، سنتحدث عن الفئات الداخلية المجهولة - المجموعة الأخيرة من الفئات المتداخلة. حظ موفق في دراستك! :)
GO TO FULL VERSION