Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

الفئات الداخلية والمتداخلة والمجهولة (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) مفيدة بشكل خاص عندما تكون الفئة ذات صلة بفئة واحدة أخرى فقط، مما يحافظ على تنظيم الكود الخاص بك وتغليفه.

challenge icon

التحدي

سهل

لنقم ببناء نظام عربة تسوق يستعرض الأنواع الثلاثة للفئات الداخلية (inner classes). ستقوم بإنشاء ShoppingCart تستخدم فئة داخلية متداخلة لتمثيل العناصر، وفئة داخلية محلية لحساب الخصومات، وفئة داخلية مجهولة لتنسيق الإيصال النهائي.

ستقوم بتنظيم الكود الخاص بك عبر ملفين:

  • ShoppingCart.java: قم بإنشاء الفئة الخارجية التي تدير تجربة التسوق. يجب أن تحتوي ShoppingCart الخاصة بك على حقل خاص customerName (String).

    داخل ShoppingCart، قم بتعريف فئة داخلية متداخلة تسمى Item مع حقلين خاصين: name (String) و price (double). امنح Item منشئًا (constructor) لتهيئة كلا الحقلين وطريقة getDetails() التي ترجع: [name]: $[price]

    تحتاج ShoppingCart الخاصة بك إلى منشئ يأخذ اسم العميل، وطريقة تسمى checkout() تقبل Item ونسبة خصم (double). داخل checkout():

    1. قم بإنشاء فئة داخلية محلية تسمى DiscountCalculator مع طريقة applyDiscount(double originalPrice) ترجع السعر بعد تطبيق نسبة الخصم.
    2. استخدم الفئة المحلية لحساب السعر النهائي.
    3. قم بإنشاء فئة داخلية مجهولة تنفذ واجهة 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، ثم طباعة النتيجة
        
    }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming