الوحدة 4: الوراثة وتعدد الأشكال والتجريد · الموضوع 9 من 17
الموضوع 9

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

الكائن الواحد يأخذ أشكالًا عدة، والاستدعاء الواحد يتصرّف بحسب الكائن الفعلي.

Shape[] shapes[0][1][2] getArea()78.524.010.5
بعد هذا الدرس ستستطيع أن:
  • تعرّف تعدد الأشكال Polymorphism بأمثلة من الواقع والبرمجة.
  • تميّز بين تعدد الأشكال الثابت (Overloading) والديناميكي (Overriding).
  • تعيد كتابة دالة موروثة في الكلاس الابن باستخدام @Override.
  • تخزّن كائنات الأبناء في متغير أو مصفوفة من نوع الأب، وتتوقّع الدالة التي تُنفَّذ.

١الفكرة

Polymorphism كلمة يونانية: Poly = كثير، وMorph = شكل. في البرمجة: أداء مهمة واحدة بطرق مختلفة، وقدرة الكائن على أن يُعامل كنوع أبيه.

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

٢نوعاه في جافا

Polymorphism Static (Compile-time)ثابت · وقت الترجمة · ربط مبكر Dynamic (Runtime)ديناميكي · وقت التشغيل · JVM ● Method overloading● Constructor overloading● Method hiding● Method overriding
الثابت يحدّده المترجِم من المعاملات. الديناميكي يحدّده JVM أثناء التشغيل من نوع الكائن الفعلي.

الثابت: التحميل الزائد

Main.java
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);    }}
Output – JavaApplication (run)×
run:
Sum of two numbers: 30
Sum of three numbers: 60
BUILD SUCCESSFUL (total time: 0 seconds)

أثناء الترجمة (قبل التنفيذ) يطابق المترجِم القيم المرسلة ويربط كل استدعاء بالدالة المناسبة؛ لذلك يُسمّى الربط المبكر Early Binding.

٣الديناميكي: إعادة كتابة الدوال Overriding

نأتي إلى الكلاس الابن ونعيد كتابة دالة ورثها من الأب، لنجعل عملها مناسبًا له. رأس الدالة لا يتغيّر (الاسم والمعاملات والنوع الراجع)، والذي يتغيّر هو الجسم فقط.
Employee- salary : double = 1500+ getSalary() : doubleHourlyEmployee- workingHours : int = 40- hourRate : double = 150+ getSalary() : doubleSalariedEmployee- bonus : double = 2500- deductions : double = 100+ getSalary() : double
🧩 الدالة getSalary بالرأس نفسه في الأب والابنين، وكل ابن يحسب الراتب بطريقته: 1500 و 6000 و 3900.
+ public- private# protected~ defaultمسطّر: staticمائل: abstract

إعادة كتابة دالة بمساعدة NetBeans

  1. داخل الكلاس الابن (مثل HourlyEmployee) انقر بالزر الأيمن، واختر Insert Code (أو Alt+Insert)، ثم من قائمة Generate اختر Override Method.
قائمة Generate وفيها Override Method
قائمة Generate‏ ← ‏Override Method
  1. حدّد الدالة التي تريد إعادة كتابتها، مثل getSalary() من الكلاس Employee، ثم اضغط Generate. يضيف NetBeans الدالة ومعها @Override، فتكتب أنت جسمها الجديد.
نافذة اختيار الدالة لإعادة كتابتها
نافذة Generate Override Methods: تحديد getSalary() من الكلاس Employee
OverloadingOverriding
أين؟في الكلاس نفسهبين الأب والابن
المعاملاتمختلفةمطابقة تمامًا
متى يُحدَّد؟وقت الترجمةوقت التشغيل

المثال التطبيقي هو التدريب 13 أدناه: متغير واحد emp يأخذ ثلاثة أشكال، فيطبع ثلاثة رواتب مختلفة.

التدريب العملي 13: التحميل الزائد ثم تغيير شكل الكائنالجزء الأول: Calc بدالتي sum. الجزء الثاني: متغير واحد emp يأخذ ثلاثة أشكال.افتح التدريب ← التدريب العملي 14: مصفوفة الأشكالShape بدالتين، وثلاثة أبناء، ومصفوفة تمرّ عليها for-each.افتح التدريب ←

توقّع وتحقّق

أجب قبل أن تنظر إلى الشرح. التصحيح فوري، وإجاباتك تُحفظ.

التحميل الزائد Overloading مثال على تعدد الأشكال:

تأمل الكود التالي،

snippet
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

تأمل الكود التالي،

snippet
class Shape {}class Circle extends Shape {    Circle(double radius) { }}class Rectangle extends Shape {    Rectangle(double w, double h) { }}

الكلاسان Circle و Rectangle يرثان الكلاس Shape. أي سطر من الأسطر التالية صحيح؟

متغير من نوع الأب Shape يمكن أن يحمل كائنًا من الابن Circle، لا العكس.

من يحدّد الدالة المنفَّذة في تعدد الأشكال الديناميكي؟

الخلاصة

انتقل إلى تقييم الدرسأسئلة التقييم، وأكمل الفراغات.ابدأ التقييم ←