📘 شرح الدرس الوحدة 5: البرمجة الشيئية · الدرس 14 من 17
الدرس 14
📋 في الخطة: الوحدة 10. التوريث والتعدد

تعدد الأشكال والتجريد

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

Shape[] shapes[0][1][2] getArea()78.524.010.5
بعد هذا الدرس ستستطيع أن:
  • تشرح مفهوم تعدد الأشكال وأنواعه.
  • تعيد كتابة دالة موروثة Overriding.
  • تنشئ كلاسًا مجردًا abstract بدوال مجردة.
  • تكتب واجهة interface وتنفذها في أكثر من كلاس.

١الفكرة

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 يأخذ ثلاثة أشكال، فيطبع ثلاثة رواتب مختلفة.

٤ما هو التجريد؟

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

هذه هي المبادئ الأربعة للبرمجة الشيئية، ونكون بهذا الدرس قد درسناها كلها.

٥الكلاس المجرد abstract class

القاعدةالتوضيح
يُعرَّف بـ abstract classلا يمكن إنشاء كائن منه: new Shape() خطأ ترجمة.
قد يحتوي دوال مجردةدالة بلا جسم تنتهي بـ ; مثل abstract double getArea();
وقد يحتوي دوالًا عادية وخصائصتُورث جاهزة للأبناء.
الابن غير المجرديجب أن يكتب كل الدوال المجردة، وإلا فهو خطأ ترجمة.
«abstract»Shape- color : String+ getColor() : String+ setColor(color : String) : void+ getArea() : double+ getPerimeter() : doubleCircle- radius : double+ Circle(radius)+ getArea() : double+ getPerimeter() : doubleRectangle- width : double- height : double+ Rectangle(w, h)+ getArea() : double+ getPerimeter() : double
🧩 اسم الكلاس المجرد ودواله المجردة تُكتب مائلة. الدوال العادية (getColor) تُورث جاهزة، والمجردة يجب على كل ابن كتابتها.
+ public- private# protected~ defaultمسطّر: staticمائل: abstract

٦متى نستخدم الكلاس المجرد؟

العنصرالوصف
abstract classكلاس لا يمكن إنشاء كائن منه مباشرة
abstract methodدالة بلا جسم، تُعرَّف فقط وتُنفَّذ في الكلاس الابن
هل توجد دالة عادية داخل الكلاس المجرد؟نعم
هل يجب على الابن تنفيذ الدوال المجردة؟نعم

ويتحقق التجريد أيضًا بالواجهات interfaces، وتتعرّف عليها لاحقًا.

٧ما هي الواجهة Interface؟

الواجهة interface قائمة بأسماء دوال بلا أجسام. أي كلاس يوقّع عليها بالكلمة implements يلتزم بكتابة جسم كل دالة فيها.
🔌 مقبس الكهرباءأي جهاز له القابس المناسب يعمل عليه، مهما كان نوعه. المقبس «واجهة» يلتزم بها الجميع.
📋 عقد العمليحدّد المهام المطلوبة من الموظف، ولا يحدّد كيف ينجزها.

تذكّر الكلاس المجرد abstract في القسم السابق: الواجهة تشبهه، لكنها في صورتها الأساسية مجردة بالكامل؛ لا تحتوي دوالًا عادية ولا خصائص متغيرة.

٨كتابة الواجهة وتنفيذها

نكتب الواجهة بالكلمة interface بدل class:

Printable.java
public interface Printable {    void printInfo();   // دالة بلا جسم}

ثم ينفّذها الكلاس بالكلمة implements، ويكتب جسم الدالة:

Student.java
public class Student implements Printable {    int id;    String name;    double mark;     public Student(int id, String name, double mark) {        this.id = id;        this.name = name;        this.mark = mark;    }     @Override    public void printInfo() {        System.out.println("Student: " + id + " - " + name + " - " + mark);    }}
«interface»Printable+ printInfo() : voidStudent~ id : int~ name : String~ mark : double+ printInfo() : voidBook~ id : int~ title : String+ printInfo() : void
🧩 في UML تُكتب الواجهة بعنوان «interface»، والسهم المتقطع ذو الرأس المثلث الفارغ يعني «ينفّذ implements».
+ public- private# protected~ defaultمسطّر: staticمائل: abstract

٩مثال: كلاسان مختلفان ينفّذان الواجهة نفسها

الطالب والكتاب لا علاقة وراثة بينهما، لكن كليهما يستطيع أن «يطبع بياناته». الواجهة تجمعهما، فنضعهما في مصفوفة واحدة من نوع Printable:

public interface Printable {    void printInfo();   // دالة بلا جسم}
public class Student implements Printable {    int id;    String name;    double mark;     public Student(int id, String name, double mark) {        this.id = id;        this.name = name;        this.mark = mark;    }     @Override    public void printInfo() {        System.out.println("Student: " + id + " - " + name + " - " + mark);    }}
public class Book implements Printable {    int id;    String title;     public Book(int id, String title) {        this.id = id;        this.title = title;    }     @Override    public void printInfo() {        System.out.println("Book: " + id + " - " + title);    }}
public class Main {    public static void main(String[] args) {        Printable[] items = {            new Student(4321, "Mohammed", 80),            new Book(1, "Java in 10 days"),            new Student(4344, "Turki", 50)        };         for (Printable p : items) {            p.printInfo();        }    }}
مخطط المشروع مستخرج من الكود. انقر على أي كلاس لفتح كوده.
«interface»PrintableStudentBookMain
«interface»Printable+ printInfo() : voidStudent~ id : int~ name : String~ mark : double+ Student(id : int, name : String, mark : double)+ printInfo() : voidBook~ id : int~ title : String+ Book(id : int, title : String)+ printInfo() : voidMain+ main(args : String[]) : void

ملفات المشروع في تبويبات كما في NetBeans. زر التشغيل يجمعها في ملف واحد ليعمل على OneCompiler.

Output – Printable (run)×
run:
Student: 4321 - Mohammed - 80.0
Book: 1 - Java in 10 days
Student: 4344 - Turki - 50.0
BUILD SUCCESSFUL (total time: 0 seconds)

p من نوع Printable، لكن في كل دورة يحمل كائنًا مختلفًا، فتُنفَّذ نسخة الكائن الفعلي: هذا تعدد الأشكال الذي درسته.

١٠تنفيذ أكثر من واجهة

الكلاس في جافا يرث كلاسًا واحدًا فقط بـ extends، لكنه يستطيع تنفيذ أكثر من واجهة بفاصلة بينها. البطة تسبح وتطير:

Duck.java
interface Swimmer {    void swim();} interface Flyer {    void fly();} class Duck implements Swimmer, Flyer {    public void swim() {        System.out.println("البطة تسبح");    }    public void fly() {        System.out.println("البطة تطير");    }} public class Main {    public static void main(String[] args) {        Duck d = new Duck();        d.swim();        d.fly();    }}
Output – JavaApplication (run)×
run:
البطة تسبح
البطة تطير
BUILD SUCCESSFUL (total time: 0 seconds)

١١الكلاس المجرد أم الواجهة؟

abstract classinterface
الكلمةextendsimplements
عدد ما يرثه/ينفّذه الكلاسكلاس واحد فقطأكثر من واجهة
الدوالمجردة وعاديةمجردة (في صورتها الأساسية)
الخصائصأي خصائصثوابت فقط
إنشاء كائن منهلالا
متى نستخدمه؟كلاسات متقاربة بينها كود مشتركقدرة مشتركة بين كلاسات مختلفة

توقّع وتحقّق

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

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، لا العكس.

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

أي سطر خطأ إذا كان Shape كلاسًا مجردًا؟

إنشاء مصفوفة من النوع المجرد مسموح؛ الممنوع إنشاء كائن منه.

الدالة المجردة:

الكلاس الابن يرث الكلاس المجرد بالكلمة:

كلاس ابن لم يكتب دالة مجردة ورثها، وهو غير مجرد:

الكلمة التي تجعل الكلاس ينفّذ واجهة:

ما الذي يجب على الكلاس الذي ينفّذ الواجهة Printable؟

أي سطر صحيح؟

إذا كان الكلاس Book ينفّذ الواجهة Printable:

لا يُنشأ كائن من الواجهة، لكن متغير الواجهة يحمل كائنًا من كلاس ينفّذها.

كم واجهة يستطيع الكلاس الواحد أن ينفّذها؟

الخلاصة

🧪 تدريبات المذكرة العملية

التدريب العملي 42: التحميل الزائد ثم تغيير شكل الكائنمن مذكرة العملي: المسألة، والخوارزمية، والمخرجات، والحل.افتح التدريب ←التدريب العملي 43: شكل مجرد بدائرة ومستطيلمن مذكرة العملي: المسألة، والخوارزمية، والمخرجات، والحل.افتح التدريب ←التدريب العملي 44: واجهة الطباعة Printableمن مذكرة العملي: المسألة، والخوارزمية، والمخرجات، والحل.افتح التدريب ←انتقل إلى تقييم الدرسأسئلة الاختيار، وصح وخطأ، وأكمل الفراغات، والأسئلة العملية.ابدأ التقييم ←