الموضوع 9
تعدد الأشكال Polymorphism
الكائن الواحد يأخذ أشكالًا عدة، والاستدعاء الواحد يتصرّف بحسب الكائن الفعلي.
بعد هذا الدرس ستستطيع أن:
- تعرّف تعدد الأشكال Polymorphism بأمثلة من الواقع والبرمجة.
- تميّز بين تعدد الأشكال الثابت (Overloading) والديناميكي (Overriding).
- تعيد كتابة دالة موروثة في الكلاس الابن باستخدام @Override.
- تخزّن كائنات الأبناء في متغير أو مصفوفة من نوع الأب، وتتوقّع الدالة التي تُنفَّذ.
١الفكرة
Polymorphism كلمة يونانية: Poly = كثير، وMorph = شكل. في البرمجة: أداء مهمة واحدة بطرق مختلفة، وقدرة الكائن على أن يُعامل كنوع أبيه.
💧 الماءسائل، وصلب إذا تجمّد، وغاز إذا غلى: مادة واحدة بأشكال عدة.
🧑 الشخصموظف في المكتب، وعميل في السوق، وراكب في الحافلة، وابن في البيت.
⏻ زر الحاسبزر واحد: يشغّل الجهاز أو يطفئه حسب حالته.
٢نوعاه في جافا
الثابت: التحميل الزائد
package polymorephism;public class Main { void sum(int x, int y) { int s = x + y; System.out.println("Sum of two numbers: " + s); } void sum(int x, int y, int z) { int s = x + y + z; System.out.println("Sum of three numbers: " + s); } public static void main(String[] args) { Main m = new Main(); m.sum(20, 10); m.sum(10, 20, 30); }}
run:
Sum of two numbers: 30
Sum of three numbers: 60
BUILD SUCCESSFUL (total time: 0 seconds)
أثناء الترجمة (قبل التنفيذ) يطابق المترجِم القيم المرسلة ويربط كل استدعاء بالدالة المناسبة؛ لذلك يُسمّى الربط المبكر Early Binding.
٣الديناميكي: إعادة كتابة الدوال Overriding
نأتي إلى الكلاس الابن ونعيد كتابة دالة ورثها من الأب، لنجعل عملها مناسبًا له. رأس الدالة لا يتغيّر (الاسم والمعاملات والنوع الراجع)، والذي يتغيّر هو الجسم فقط.
+ public- private# protected~ defaultمسطّر: staticمائل: abstract
- نكتب
@Overrideقبل الدالة في الابن، فيتحقق المترجِم أنها فعلًا موجودة في الأب. - في NetBeans: Insert Code ← Override Method ← اختر الدالة (انظر الخطوات بالصور أدناه).
- لا يمكن إعادة كتابة دالة private أو static أو final.
- عند الاستدعاء يحدّد JVM أثناء التشغيل أي نسخة تُنفَّذ حسب الكائن الفعلي؛ لذلك يُسمّى الربط المتأخر.
إعادة كتابة دالة بمساعدة NetBeans
- داخل الكلاس الابن (مثل HourlyEmployee) انقر بالزر الأيمن، واختر Insert Code (أو Alt+Insert)، ثم من قائمة Generate اختر Override Method.

- حدّد الدالة التي تريد إعادة كتابتها، مثل getSalary() من الكلاس Employee، ثم اضغط Generate. يضيف NetBeans الدالة ومعها @Override، فتكتب أنت جسمها الجديد.

| Overloading | Overriding | |
|---|---|---|
| أين؟ | في الكلاس نفسه | بين الأب والابن |
| المعاملات | مختلفة | مطابقة تمامًا |
| متى يُحدَّد؟ | وقت الترجمة | وقت التشغيل |
المثال التطبيقي هو التدريب 13 أدناه: متغير واحد emp يأخذ ثلاثة أشكال، فيطبع ثلاثة رواتب مختلفة.
توقّع وتحقّق
أجب قبل أن تنظر إلى الشرح. التصحيح فوري، وإجاباتك تُحفظ.
التحميل الزائد Overloading مثال على تعدد الأشكال:
تأمل الكود التالي،
class Employee { private double salary = 1500; public double getSalary() { return salary; }}class HourlyEmployee extends Employee { private int workingHours = 40; private double hourRate = 150; @Override public double getSalary() { return workingHours * hourRate; }} // main داخل الدالةEmployee e = new HourlyEmployee();System.out.println(e.getSalary());
ماذا سيكون الناتج عند تنفيذ آخر سطرين؟
المتغير e من نوع الأب Employee، لكن الكائن الفعلي من HourlyEmployee، فتُنفَّذ نسخته: 40 × 150 = 6000.0
تأمل الكود التالي،
class Shape {}class Circle extends Shape { Circle(double radius) { }}class Rectangle extends Shape { Rectangle(double w, double h) { }}
الكلاسان Circle و Rectangle يرثان الكلاس Shape. أي سطر من الأسطر التالية صحيح؟
متغير من نوع الأب Shape يمكن أن يحمل كائنًا من الابن Circle، لا العكس.
من يحدّد الدالة المنفَّذة في تعدد الأشكال الديناميكي؟
الخلاصة
- تعدد الأشكال: استدعاء واحد بسلوكيات مختلفة.
- ثابت (Overloading، وقت الترجمة) وديناميكي (Overriding، وقت التشغيل).
- متغير أو مصفوفة من نوع الأب تحمل كائنات الأبناء.
- تُنفَّذ نسخة الكائن الفعلي لا نسخة نوع المتغير.