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

الـ Upcasting والـ Downcasting

جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 30 من 87.

في الدرس السابق، رأيت كيف يمكن لمتغير من نوع الأب (parent-type) أن يحمل كائناً من نوع الابن (child object): Animal myPet = new Dog(). تُسمى عملية التعيين هذه upcasting، وهي تحدث تلقائياً لأن الـ Dog هو دائماً Animal.

Upcasting ينتقل للأعلى في تسلسل الوراثة الهرمي. إنه ضمني وآمن دائماً:

Dog dog = new Dog();
Animal animal = dog;  // Upcasting - تلقائي

عندما تقوم بالتحويل للأعلى (upcast)، يمكنك فقط الوصول إلى الدوال (methods) المعرفة في الفئة الأب (parent class). تصبح الدوال الخاصة بالفئة الابن مخفية، على الرغم من أن الكائن لا يزال Dog في جوهره.

يسير Downcasting في الاتجاه المعاكس، حيث يقوم بتحويل مرجع من النوع الأب (parent) مرة أخرى إلى نوع الابن (child). يتطلب هذا عملية تحويل صريحة (explicit cast) لأنه ليس آمناً دائماً:

Animal animal = new Dog();  // تحويل للأعلى (Upcasting)
Dog dog = (Dog) animal;     // تحويل للأسفل (Downcasting) - يتطلب تحويلاً صريحاً
dog.bark();                 // الآن يمكننا الوصول إلى دوال Dog

عملية التحويل السفلي (Downcasting) محفوفة بالمخاطر. إذا لم يكن الكائن في الواقع من النوع الذي تقوم بالتحويل إليه، ستقوم Java بطرح ClassCastException في وقت التشغيل:

Animal animal = new Cat();
Dog dog = (Dog) animal;  // يتم تجميعه، ولكنه ينهار عند التشغيل!

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

challenge icon

التحدي

سهل

لنقم ببناء نظام مشغل وسائط يوضح كلاً من الـ upcasting والـ downcasting بشكل عملي. ستقوم بإنشاء تسلسل هرمي لأنواع الوسائط وتتدرب على التحويل بين مراجع الأب والابن.

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

  • Media.java: قم بإنشاء الفئة الأساسية (base class) التي تشترك فيها جميع أنواع الوسائط. كل عنصر وسائط لديه حقل title من نوع (String). قم بتضمين constructor لتهيئته، وطريقة getter تسمى getTitle()، وطريقة play() تطبع: "Playing: " + title
  • Song.java: قم بإنشاء فئة ترث من Media. تحتوي الأغاني على حقل إضافي artist من نوع (String). استخدم super لتهيئة الأب. قم بعمل override لطريقة play() لتطبع: "Playing song: " + title + " by " + artist. أضف طريقة showArtist() تطبع: "Artist: " + artist
  • Podcast.java: قم بإنشاء فئة أخرى ترث من Media. تحتوي البودكاست على حقل episode من نوع (int). قم بعمل override لطريقة play() لتطبع: "Playing podcast: " + title + " - Episode " + episode. أضف طريقة showEpisode() تطبع: "Episode: " + episode
  • Main.java: قم بتوضيح الـ upcasting والـ downcasting مع أنواع الوسائط الخاصة بك. ستتلقى أربعة مدخلات: عنوان الأغنية، اسم الفنان، عنوان البودكاست، ورقم الحلقة.

    أولاً، قم بإنشاء Song وقم بعمل upcast لها إلى مرجع Media. استدعِ play() على هذا المرجع لرؤية تعدد الأشكال (polymorphism) في وقت التشغيل.

    بعد ذلك، قم بعمل downcast لمرجع Media هذا مرة أخرى إلى Song واستدعِ showArtist() للوصول إلى الطريقة الخاصة بالابن.

    ثم، قم بإنشاء Podcast وقم بعمل upcast له إلى مرجع Media. استدعِ play() على هذا المرجع.

    أخيراً، قم بعمل downcast لمرجع Media هذا مرة أخرى إلى Podcast واستدعِ showEpisode().

ستتلقى أربعة مدخلات: عنوان الأغنية (String)، اسم الفنان (String)، عنوان البودكاست (String)، ورقم الحلقة (int).

يجب أن يظهر مخرجك أربعة أسطر - سطران للأغنية (مخرج التشغيل ومعلومات الفنان) وسطران للبودكاست (مخرج التشغيل ومعلومات الحلقة). لاحظ كيف يسمح لك الـ upcasting بالتعامل مع أنواع الوسائط المختلفة بشكل موحد، بينما يسمح لك الـ downcasting بالوصول إلى ميزاتها الفريدة!

ورقة مرجعية

في لغة Java، ينتقل التحويل للأعلى (upcasting) إلى أعلى في تسلسل الوراثة ويحدث تلقائيًا. يمكن تعيين كائن من النوع الابن (child) إلى متغير من النوع الأب (parent):

Dog dog = new Dog();
Animal animal = dog;  // Upcasting - تلقائي

عند إجراء التحويل للأعلى، يمكنك فقط الوصول إلى الأساليب (methods) المعرفة في الفئة الأب (parent class)، على الرغم من أن الكائن لا يزال من النوع الابن في الأصل.

يقوم التحويل للأسفل (downcasting) بتحويل مرجع الأب مرة أخرى إلى نوع الابن. يتطلب ذلك تحويلاً صريحًا (explicit cast):

Animal animal = new Dog();  // Upcasting
Dog dog = (Dog) animal;     // Downcasting - تحويل صريح مطلوب
dog.bark();                 // الآن يمكننا الوصول إلى أساليب Dog

التحويل للأسفل محفوف بالمخاطر. إذا لم يكن الكائن في الواقع من النوع الذي تقوم بالتحويل إليه، فإن Java ستطرح استثناءً من نوع ClassCastException في وقت التشغيل:

Animal animal = new Cat();
Dog dog = (Dog) animal;  // يتم التجميع بنجاح، لكنه ينهار في وقت التشغيل!

لا يمكن للمترجم (compiler) اكتشاف هذا الخطأ لأنه يرى فقط الأنواع المعلنة. لا يُعرف نوع الكائن الفعلي إلا عند تشغيل البرنامج.

جرّب بنفسك

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // قراءة المدخلات
        String songTitle = scanner.nextLine();
        String artistName = scanner.nextLine();
        String podcastTitle = scanner.nextLine();
        int episodeNumber = scanner.nextInt();
        
        // TODO: قم بإنشاء Song وقم بعمل upcast لها إلى مرجع Media
        
        // TODO: استدعِ ()play على مرجع Media (يوضح تعدد الأشكال polymorphism)
        
        // TODO: قم بعمل downcast لمرجع Media ليعود إلى Song
        
        // TODO: استدعِ ()showArtist على مرجع Song
        
        // TODO: قم بإنشاء Podcast وقم بعمل upcast له إلى مرجع Media
        
        // TODO: استدعِ ()play على مرجع Media
        
        // TODO: قم بعمل downcast لمرجع Media ليعود إلى Podcast
        
        // TODO: استدعِ ()showEpisode على مرجع Podcast
    }
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming