تعدد الأشكال في Java (ثابتة وديناميكية)

⚡ ملخص ذكي

تعدد الأشكال في Java يُتيح هذا الأسلوب واجهة واحدة لتمثيل نماذج متعددة، بحيث يتصرف اسم دالة واحد بشكل مختلف عبر الفئات ذات الصلة. وهو ركن أساسي من أركان التصميم الكائني التوجه، إذ يُعزز المرونة، وإعادة استخدام التعليمات البرمجية، والتوسع السلس للأنظمة المصرفية والطبية وأنظمة المؤسسات القديمة.

  • 🎭 أشكال عديدة، اسم واحد: تعدد الأشكال في Java يسمح هذا بتوزيع نفس اسم الطريقة إلى تطبيقات مختلفة بناءً على نوع الكائن الفعلي في وقت التنفيذ.
  • 🔀 نكهتين: يستخدم تعدد الأشكال في وقت الترجمة التحميل الزائد للأساليب، بينما يستخدم تعدد الأشكال في وقت التشغيل تجاوز الأساليب من خلال الوراثة وJVM.
  • ⛓️ الربط الثابت مقابل الربط الديناميكي: يتم حل الاستدعاءات المحملة بشكل زائد في وقت الترجمة من خلال الربط الثابت، بينما يتم حل الاستدعاءات المتجاوزة في وقت التشغيل من خلال الربط الديناميكي.
  • قوة الكلمات المفتاحية الفائقة: تتيح الكلمة المفتاحية super للفئة الفرعية استدعاء تطبيق الفئة الأصلية قبل إضافة سلوكها المتخصص الخاص فوقها.
  • 🧪 نطاق الوصول في العالم الحقيقي: تعتمد الحسابات المصرفية، والتسلسل الهرمي لموظفي المستشفيات، ومحركات عرض الأشكال، جميعها على تعدد الأشكال لتلبية المتطلبات الجديدة دون كسر التعليمات البرمجية المختبرة.

تعدد الأشكال في Java

ما هو تعدد الأشكال في Java?

تعدد الأشكال في Java يحدث تعدد الأشكال عندما ترتبط فئة أو أكثر من الفئات أو الكائنات ببعضها البعض عن طريق الوراثة. وهي قدرة الكائن على اتخاذ أشكال متعددة. تسمح الوراثة للمستخدمين بتوريث السمات والأساليب، بينما يستخدم تعدد الأشكال هذه الأساليب لأداء مهام مختلفة. الهدف هو التواصل، وإن اختلفت الأساليب المتبعة.

على سبيل المثال، يُستخدم الهاتف الذكي للتواصل. قد تكون وسيلة التواصل مكالمة هاتفية، أو رسالة نصية، أو رسالة مصورة، أو بريدًا إلكترونيًا. الهدف واحد، وهو التواصل، لكن الأسلوب يختلف. هذا السلوك هو جوهر... تعدد الأشكالسنتعلم الآن تعدد الأشكال في Java مع أمثلة برمجية ورسوم بيانية وشروحات خطوة بخطوة.

انقر اضغط هنا إذا لم يكن من الممكن الوصول إلى الفيديو

لماذا نستخدم تعدد الأشكال في Java?

تعدد الأشكال في Java يُستخدم هذا النمط لأنه يجعل الكود مرنًا وقابلًا لإعادة الاستخدام وأسهل في الصيانة. إذ يمكن لمتغير مرجعي واحد من نوع أب أن يشير إلى عدة كائنات فرعية، وبالتالي يتكيف الاستدعاء نفسه مع أي فئة فرعية يتم توفيرها. يحافظ هذا النمط على الكود المختبر دون تغيير بينما تتم إضافة سلوكيات جديدة من خلال فئات فرعية متخصصة، وهذا تحديدًا هو سبب اعتماد أطر عمل مثل Spring و Jakarta EE عليه.

Java تعدد الأشكال في OOPs مع مثال

لنفترض وجود فئة أصلية واحدة تسمى الحساب يحتوي على وظائف للإيداع والسحب. يحتوي صنف الحساب على صنفين فرعيين. عملية الإيداع والسحب متطابقة لحسابات التوفير والحسابات الجارية، لذا ستعمل الطرق الموروثة من صنف الحساب.

الميراث الوحيد في Java

Java مثال تعدد الأشكال

التغيير في متطلبات البرامج

طرأ تغيير على مواصفات المتطلبات، وهو أمر شائع في صناعة البرمجيات. يجب إضافة وظيفة لحساب مصرفي مميز مزود بخاصية السحب على المكشوف. السحب على المكشوف هو خدمة تُمكّن العميل من سحب مبلغ يتجاوز الرصيد المتاح في الحساب. تتطلب طريقة السحب لهذا الحساب المميز إعادة تنفيذ جديدة، بينما يبقى الكود المُختبَر في حسابات التوفير والحسابات الجارية دون تغيير. هذه العزلة هي ميزة... وجه الفتاة.

Java تعدد الأشكال

الخطوة 1) عند استدعاء طريقة السحب لحساب التوفير، يتم تنفيذ الطريقة المحددة في فئة الحساب الأصلية.

Java تعدد الأشكال

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

Java تعدد الأشكال

طريقة التجاوز في Java

إعادة تعريف الأساليب هي عملية إعادة تعريف أسلوب من فئة أساسية داخل فئة فرعية. تحتفظ الفئة الفرعية بنفس الأسلوب.tract، ولكنه يوفر تطبيقًا جديدًا يتم اختياره عند تشغيل البرنامج.

قواعد لتجاوز الطريقة

  • يجب أن تتطابق توقيعات الأساليب، أي اسم الأسلوب وقائمة المعلمات ونوع الإرجاع، تمامًا.
  • يمكن للطريقة المُعاد تعريفها توسيع نطاق الوصول، ولكن لا يمكنها تضييقه. إذا كانت الطريقة خاصة في الفئة الأساسية، فيمكن للفئة الفرعية جعلها عامة، ولكن ليس العكس.

قواعد لتجاوز الطريقة في Java

مثال

class Doctor{
  public void treatPatient(){
  // treatPatient method
  
}
class Surgeon extends Doctor{
  public void treatPatient(){
  // treatPatient method
  }
}
class Run{
  public static void main (String args[]){
    Doctor doctorObj = new Doctor();
    // treatPatient method in class Doctor will be executed
    doctorObj.treatPatient();
   
    Surgeon surgeonObj = new Surgeon();
    // treatPatient method in class Surgeon will be executed
    surgeonObj.treatPatient();
  }
}

الفرق بين تحميل الأساليب وتجاوز الأساليب

يحدث تحميل الأساليب داخل نفس الفئة بنفس اسم الأسلوب ولكن بتوقيعات مختلفة. أما إعادة تعريف الأساليب فتحدث عبر علاقة بين فئة أصلية وفئة فرعية، حيث تعيد الفئة الفرعية تعريف أسلوب مُعلن في الفئة الأصلية.

طريقة التحميل الزائد
أسلوب التجاوز
يُعد تحميل الأساليب جزءًا من نفس الفئة، حيث يكون لأكثر من أسلوب نفس الاسم ولكن بتوقيعات مختلفة. إعادة تعريف الأساليب هي عملية إعادة تعريف أحد الأساليب في الفئة الأصلية داخل الفئة الفرعية. في هذه الحالة، يبقى توقيع الأسلوب كما هو.
مثلا:

void sum (int a , int b);
void sum (int a , int b, int c);
void sum (float a, double b);
مثلا:

class X{
  public int sum(){
     // some code
  }
}
class Y extends X{
  public int sum(){
    //overridden method
    //signature is same
 }
}

ما هو تعدد الأشكال الديناميكي؟

يُعدّ التعدد الديناميكي في البرمجة الكائنية آليةً تسمح بتعريف عدة دوال بنفس الاسم والتوقيع في كلٍّ من الفئة الأصلية والفئة الفرعية. ويتم تحديد استدعاء الدالة المُعاد تعريفها أثناء التشغيل من خلال الربط الديناميكي داخل آلة جافا الافتراضية.

مثال على تعدد الأشكال الديناميكي

يمكن لمتغير مرجعي من الفئة الأصلية أن يشير إلى كائن من فئة فرعية:

 Doctor obj = new Surgeon();

انظر إلى العبارة التالية:

obj.treatPatient();

هنا المتغير المرجعي الكائنات ينتمي إلى الفئة الأصلية، لكن الكائن الذي يشير إليه ينتمي إلى الفئة الفرعية، كما هو موضح في مخطط تعدد الأشكال.

الاتصال obj.treatPatient() سيتم تنفيذ دالة treatPatient() الخاصة بالفئة الفرعية Surgeon. عند استخدام مرجع فئة أساسية لاستدعاء دالة، يتم تحديد الدالة التي سيتم استدعاؤها بواسطة JVM بناءً على الكائن الفعلي الذي يشير إليه المرجع. على الرغم من الكائنات يشير هذا إلى الطبيب، ويستدعي دالة الجراح، لأنه يشير إلى كائن جراح. ويتم اتخاذ القرار أثناء وقت التشغيل، ولهذا السبب يُطلق عليه هذا الاسم. تعدد الأشكال الديناميكي أو تعدد الأشكال في وقت التشغيل.

الفرق بين تعدد الأشكال الثابت والديناميكي

تعدد الأشكال الساكن في Java يُعدّ تعدد الأشكال نوعًا من أنواع تعدد الأشكال التي تجمع المعلومات اللازمة لاستدعاء طريقة ما في وقت الترجمة، بينما يُعدّ تعدد الأشكال الديناميكي نوعًا من أنواع تعدد الأشكال التي تجمع المعلومات اللازمة لاستدعاء طريقة ما في وقت التشغيل.

تعدد الأشكال الثابت
تعدد الأشكال الديناميكي
يتعلق الأمر بتحميل الأساليب بشكل زائد. يتعلق الأمر بتجاوز الأساليب.
تُحلّ الأخطاء، إن وُجدت، أثناء عملية الترجمة. وبما أن الكود لا يُنفّذ أثناء الترجمة، فإن الاسم ثابت.

مثلا:

void sum (int a , int b);
void sum (float a, double b);
int sum (int a, int b); //compiler gives error.
عندما يستدعي متغير مرجعي دالةً مُعاد تعريفها، فإن الدالة المراد استدعاؤها تُحدد بناءً على الكائن الذي يشير إليه المتغير المرجعي. ولا يمكن تحديد ذلك إلا أثناء تشغيل البرنامج، ولهذا السبب يكون الاسم ديناميكيًا.

مثلا:

//reference of parent pointing to child object
 Doctor obj = new Surgeon();
// method of child called
obj.treatPatient();

الكلمة الرئيسية الفائقة في Java

ماذا لو أرادت دالة treatPatient في فئة Surgeon تنفيذ الوظائف المُعرّفة في فئة Doctor، ثم القيام بسلوكها الخاص؟ في هذه الحالة، الكلمة المفتاحية super يمكن استخدامها للوصول إلى طرق الفئة الأصلية من الفئة الفرعية. يمكن كتابة طريقة TreatPatient في فئة Surgeon على النحو التالي:

treatPatient(){
   super.treatPatient();
     //add code specific to Surgeon
}

يمكن استخدام الكلمة المفتاحية super للوصول إلى أي عنصر بيانات أو دالة من الفئة الأصلية داخل الفئة الفرعية. سنتعرف لاحقًا على الكلمة المفتاحية super. وراثةوتعدد الأشكال في Java مع أمثلة البرامج.

مثال: لتعلم الوراثة، وتعدد الأشكال، والكلمة المفتاحية الفائقة

الخطوة 1) انسخ الكود التالي إلى المحرر.

public class Test{
     public static void main(String args[]){
        X x= new X();
        Y y = new Y();
        y.m2();
        //x.m1();
        //y.m1();
        //x = y;// parent pointing to object of child
        //x.m1() ;
        //y.a=10;
   }
}
class X{
   private int a;
   int b;
      public void m1(){
       System.out.println("This is method m1 of class X");
     }
}
class Y extends X{
      int c; // new instance variable of class Y
         public void m1(){
            // overridden method
            System.out.println("This is method m1 of class Y");
        }
       public void m2(){
           super.m1();
           System.out.println("This is method m2 of class Y");
      }
}

الخطوة 2) احفظ الكود، ثم قم بتجميعه وتشغيله. لاحظ الناتج.

الخطوة 3) قم بإلغاء التعليق عن الأسطر من 6 إلى 9. احفظ الكود، ثم قم بتجميعه وتشغيله. لاحظ الناتج.

الخطوة 4) قم بإلغاء التعليق على السطر 10. احفظ الكود وقم بتجميعه.

الخطوة 5) الخطأ = ؟ هذا لأن الفئة الفرعية لا تستطيع الوصول إلى الأعضاء الخاصة بالفئة الأصلية.

الأسئلة الشائعة

تعدد الأشكال في Java إن قدرة الكائن على اتخاذ أشكال متعددة هي خاصية أساسية في البرمجة الكائنية. يمكن لنفس اسم الدالة تشغيل تعليمات برمجية مختلفة اعتمادًا على الكائن الفرعي الذي تتم الإشارة إليه أثناء التشغيل، وهذا هو السلوك الجوهري للبرمجة الكائنية.

Java يدعم هذا النظام تعدد الأشكال في وقت الترجمة من خلال تحميل الأساليب، وتعدد الأشكال في وقت التشغيل من خلال تجاوز الأساليب. يتم حل التحميل الزائد في وقت الترجمة عبر الربط الثابت، بينما يتم حل التجاوز في وقت التشغيل عبر الربط الديناميكي الذي تتولى معالجته آلة جافا الافتراضية (JVM).

يربط الربط الثابت استدعاء الدالة بتعريفها في وقت الترجمة، ويُستخدم لتحميل الدوال الزائد. أما الربط الديناميكي فيربط الاستدعاء في وقت التشغيل بناءً على نوع الكائن الفعلي، وهو ما يدعم تجاوز الدوال. Java.

تتيح الكلمة المفتاحية super للفئة الفرعية استدعاء الدالة المُعاد تعريفها أو الوصول إلى عنصر بيانات من الفئة الأصلية. وهذا يسمح للفئة الفرعية بإعادة استخدام منطق الفئة الأصلية ثم إضافة سلوكيات مُخصصة فوقها.

يمكن لبرامج الذكاء الاصطناعي التعليمية مثل ChatGPT و GitHub Copilot Chat شرح تعدد الأشكال في Java باستخدام تشبيهات مخصصة، قم بإنشاء مسائل تدريبية، وتصفح أشجار الوراثة، واختبر المتعلمين حول الربط الثابت مقابل الربط الديناميكي لتحقيق إتقان أسرع.

نعم. يمكن لمساعدي إعادة هيكلة التعليمات البرمجية المدعومين بالذكاء الاصطناعي، مثل Cursor وSourcegraph Cody وIntelliJ AI Assistant، اكتشاف الشروط المكررة، واقتراح تسلسلات هرمية للفئات، وإعادة كتابة التعليمات البرمجية القديمة. Java قم بكتابة الكود بحيث يؤدي تجاوز الأسلوب إلى استبدال سلاسل if-else المتكررة.

تلخيص هذه التدوينة بـ: