الفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)
جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 49 من 87.
تسمح لك Java بتعريف فئات (classes) داخل فئات أخرى. تساعد هذه الفئات الداخلية (inner classes) في تنظيم الكود من خلال تجميع الوظائف ذات الصلة معاً، ويمكنها الوصول إلى أعضاء الفئة الخارجية مباشرةً.
تُعرَّف الفئة الداخلية المتداخلة كعضو في فئة أخرى. ولديها صلاحية الوصول إلى جميع الحقول والأساليب الخاصة بالفئة الخارجية، بما في ذلك العناصر private:
class Outer {
private String message = "Hello";
class Inner {
void display() {
System.out.println(message); // يمكنه الوصول إلى الحقل الخاص
}
}
}
// الاستخدام
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display(); // يطبع: Helloيتم تعريف الفئة الداخلية المحلية (local inner class) داخل دالة (method). ولا يمكن استخدامها إلا داخل تلك الدالة، ويمكنها الوصول إلى المتغيرات المحلية (local variables) التي تكون نهائية فعلياً (effectively final):
void process() {
String prefix = "Result: ";
class LocalHelper {
void print(int value) {
System.out.println(prefix + value);
}
}
new LocalHelper().print(42);
}تعد الفئة الداخلية المجهولة (anonymous inner class) فئة بدون اسم، يتم إنشاؤها وتهيئتها في تعبير واحد. وتُستخدم عادةً لتنفيذ الواجهات (interfaces) أو توسيع الفئات (classes) بشكل فوري:
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
};تعد الفئات الداخلية (Inner classes) مفيدة بشكل خاص عندما تكون الفئة ذات صلة بفئة واحدة أخرى فقط، مما يحافظ على تنظيم الكود الخاص بك وتغليفه.
التحدي
سهللنقم ببناء نظام عربة تسوق يستعرض الأنواع الثلاثة للفئات الداخلية (inner classes). ستقوم بإنشاء ShoppingCart تستخدم فئة داخلية متداخلة لتمثيل العناصر، وفئة داخلية محلية لحساب الخصومات، وفئة داخلية مجهولة لتنسيق الإيصال النهائي.
ستقوم بتنظيم الكود الخاص بك عبر ملفين:
ShoppingCart.java: قم بإنشاء الفئة الخارجية التي تدير تجربة التسوق. يجب أن تحتوي ShoppingCart الخاصة بك على حقل خاصcustomerName(String).داخل ShoppingCart، قم بتعريف فئة داخلية متداخلة تسمى
Itemمع حقلين خاصين:name(String) وprice(double). امنح Item منشئًا (constructor) لتهيئة كلا الحقلين وطريقةgetDetails()التي ترجع:[name]: $[price]تحتاج ShoppingCart الخاصة بك إلى منشئ يأخذ اسم العميل، وطريقة تسمى
checkout()تقبل Item ونسبة خصم (double). داخلcheckout():- قم بإنشاء فئة داخلية محلية تسمى
DiscountCalculatorمع طريقةapplyDiscount(double originalPrice)ترجع السعر بعد تطبيق نسبة الخصم. - استخدم الفئة المحلية لحساب السعر النهائي.
- قم بإنشاء فئة داخلية مجهولة تنفذ واجهة
Formatter(ستقوم بتعريفها) لتنسيق الإيصال. يجب أن تقوم الفئة المجهولة بتجاوز (override) طريقةformat()التي ترجع سلسلة نصية للإيصال متعددة الأسطر.
يجب أن ترجع طريقة
checkout()سلسلة نصية للإيصال المنسق بهذا التنسيق الدقيق:--- Receipt --- Customer: [customerName] Item: [item details from getDetails()] Discount: [discount]% Final Price: $[calculated price]- قم بإنشاء فئة داخلية محلية تسمى
Formatter.java: قم بتعريف واجهة بسيطة مع طريقة واحدة:String format(). سيتم تنفيذ هذه الواجهة بواسطة فئتك الداخلية المجهولة داخل طريقة checkout.Main.java: ابعث الحياة في عربة التسوق الخاصة بك! ستتلقى ثلاثة مدخلات: اسم العميل (String)، واسم العنصر (String)، وسعر العنصر (double). نسبة الخصم ثابتة عند10.0.قم بإنشاء ShoppingCart باسم العميل. ثم قم بإنشاء Item باستخدام صيغة مرجع الفئة الخارجية:
cart.new Item(name, price). استدعِcheckout()مع العنصر والخصم، وقم بطباعة الإيصال المرتجع.
ستتلقى ثلاثة مدخلات بالترتيب: اسم العميل، واسم العنصر، وسعر العنصر.
على سبيل المثال، إذا كانت المدخلات هي Alice و Laptop و 1000.0، فإن السعر النهائي بعد خصم 10% سيكون 900.0.
لاحظ كيف يخدم كل نوع من الفئات الداخلية غرضًا مختلفًا: الفئة المتداخلة تجمع البيانات ذات الصلة مع الفئة الخارجية، والفئة المحلية تغلف المنطق المطلوب فقط داخل طريقة واحدة، والفئة المجهولة توفر تنفيذًا سريعًا لمرة واحدة لواجهة ما!
ورقة مرجعية
تسمح لك Java بتعريف فئات داخل فئات أخرى. تساعد هذه الفئات الداخلية (inner classes) في تنظيم الكود من خلال تجميع الوظائف ذات الصلة معاً، ويمكنها الوصول إلى أعضاء الفئة الخارجية مباشرةً.
الفئة الداخلية المتداخلة (Nested Inner Class)
تُعرف الفئة الداخلية المتداخلة كعضو في فئة أخرى. ولديها صلاحية الوصول إلى جميع حقول وطرق (methods) الفئة الخارجية، بما في ذلك الحقول الخاصة (private):
class Outer {
private String message = "Hello";
class Inner {
void display() {
System.out.println(message); // يمكن الوصول إلى الحقل الخاص
}
}
}
// الاستخدام
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display(); // يطبع: Helloالفئة الداخلية المحلية (Local Inner Class)
تُعرف الفئة الداخلية المحلية داخل طريقة (method). ولا يمكن استخدامها إلا داخل تلك الطريقة، ويمكنها الوصول إلى المتغيرات المحلية التي تكون نهائية فعلياً (effectively final):
void process() {
String prefix = "Result: ";
class LocalHelper {
void print(int value) {
System.out.println(prefix + value);
}
}
new LocalHelper().print(42);
}الفئة الداخلية المجهولة (Anonymous Inner Class)
الفئة الداخلية المجهولة هي فئة بدون اسم، يتم إنشاؤها وتجسيدها (instantiated) في تعبير واحد. تُستخدم عادةً لتنفيذ الواجهات (interfaces) أو توسيع الفئات بشكل فوري:
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
};الفئات الداخلية مفيدة بشكل خاص عندما تكون الفئة ذات صلة بفئة واحدة أخرى فقط، مما يحافظ على تنظيم الكود وتغليفه.
جرّب بنفسك
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// قراءة المدخلات
String customerName = scanner.nextLine();
String itemName = scanner.nextLine();
double itemPrice = scanner.nextDouble();
double discount = 10.0;
// TODO: إنشاء ShoppingCart باسم العميل
// TODO: إنشاء Item باستخدام: cart.new Item(itemName, itemPrice)
// TODO: استدعاء checkout() مع item و discount، ثم طباعة النتيجة
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةمقدمة في الـ OOPالفئات (Classes) مقابل الكائنات (Objects)الكلمة المفتاحية thisالدوال (Methods)الحقول (Attributes)دالة البناء (Constructor)تعدد أشكال دالة البناء (Constructor Overloading)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسية (extends)الكلمة المفتاحية superإعادة تعريف الدوال (@Override)تسلسل المنشئات (Constructor Chaining)كلاس Objectالوراثة الأحادية ومتعددة المستوياتلماذا لا توجد وراثة متعددة للكلاساتمراجعة - هيكلية الموظفين7الدوال الخاصة وفئة Object
دالة toString()equals() و hashCode()دالة clone()compareTo() و Comparableواجهة Comparatorمراجعة - الترتيب المخصص2محددات الوصول والتغليف
نظرة عامة على مستويات الوصولدوال Getter و Setterإخفاء البياناتالكلمة المفتاحية finalمراجعة - مدير الحساب البنكي5تعدد الأشكال (Polymorphism)
أساسيات الـ Method Overloadingالـ Method Overriding (في وقت التشغيل)الـ Upcasting والـ Downcastingمعامل الـ instanceofالفئات والدوال المجردة (Abstract)مراجعة - حاسبة الأشكال8مفاهيم OOP المتقدمة
الـ Composition مقابل الـ Inheritanceالـ Aggregation مقابل الـ Compositionالفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)الـ Enums ودوالهاالـ Records (Java 16+)الـ Sealed Classes (Java 17+)11أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Builderنمط Observerنمط Strategy3خصائص الفئة والعضو الساكن
المتغيرات الخاصة بالكائن مقابل المتغيرات الساكنةالدوال الساكنةالكتل البرمجية الساكنةالثوابت (static final)ملخص - العداد والأدوات المساعدة6الواجهات (Interfaces) والأصناف المجردة (Abstract Classes)
مقدمة في الواجهات (Interfaces)تنفيذ الواجهات (Interfaces)تنفيذ واجهات متعددةالـ Default و Static في الواجهاتالأصناف المجردة مقابل الواجهاتالواجهات الوظيفية (Functional Interfaces)مراجعة - نظام الدفع9الأنواع العامة (Generics)
مقدمة في الأنواع العامةالأصناف العامة (Generic Classes)الدوال العامة (Generic Methods)معاملات الأنواع المقيدةالرموز البديلة (?, extends, super)مراجعة - الحاويات العامة12أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكرر