القائمة الرئيسية

الصفحات

تعلم البرمجه تعلم لغه البرمجه Javaالدرس السادس 6

لم البرمجه من الصفر حتى الختراف


تعلم البرمجه  تعلم لغه البرمجه الجافا الدرس السادس 6


Java
مفهوم الـ Overriding


مفهوم الـ Overriding
في الدرس السابق, شاهدت كيف أن الـ Subclass يرث المتغيرات و الدوال الموجودة في الـ Superclass. و تعلمت أيضاً أنه يمكن للـ Subclass إعادة تعريف أي دالة ورثها من الـ Superclass شرط أن لا تكون معرفة كـ final, و كتابة الكلمة @Override قبل تعريفها من جديد حتى تتفادى حدوث مشاكل عند ترجمة الكود.

Override: تعني تعريف الدالة التي ورثها الـ Subclass من الـ Superclass من جديد, هذه الدالة الجديدة تكون مشابهة للدالة الموروثة من حيث الشكل فقط, أي لها نفس الإسم و النوع و عدد الباراميترات, لكن محتواها مختلف.

الهدف الحقيقي من الـ Overriding هو إتاحة الفرصة للـ Subclass ليعرف الدوال حسب حاجته.

في دروس متقدمة سنرث من كلاسات جاهزة في جافا, و نفعل Override للدوال الموجودة فيها لكي تناسب التطبيقات التي سنقوم ببنائها.


مثال
الآن لنفترض أننا قمنا بتعريف كلاس إسمه Country, يحتوي على دالة إسمها language().
بعدها قمنا بتعريف ثلاث كلاسات, و كلها ترث من Country, إذاً كلها ستحتوي على الدالة language().
هنا الفكرة أن أي كلاس يرث من Country قد يضطر إلى تعريف الدالة language() من جديد حتى تناسبه.

بعد إنشاء هذه الكلاسات, سنقوم بإنشاء الكلاس Test لتجربتهم.

Country.java
package inheritance;

public class Country {     // هنا, هذا الكلاس يعتبر الكلاس الأساسي لأي دولة في العالم, إذاً يجب أن يرثه أي كلاس يمثل دولة

    public void language() {
        System.out.println("English");   // هنا قمنا بوضع اللغة الإنجليزية كلغة إفتراضية لجميع البلدان
    }

}

Australia.java
package inheritance;

public class Australia extends Country {

    // من جديد لأن اللغة الإنجليزية هي لغة أستراليا language() هنا لا داعي لتعريف الدالة

}

Lebanon.java
package inheritance;

public class Lebanon extends Country {

    // من جديد لأن اللغة الإنجليزية ليست لغة لبنان language() هنا يجب تعريف الدالة
    @Override
    public void language() {
        System.out.println("Arabic");
    }

}

Spain.java
package inheritance;

public class Spain extends Country {

    // من جديد لأن اللغة الإنجليزية ليست لغة إسبانيا language() هنا يجب تعريف الدالة
    @Override
    public void language() {
        System.out.println("Spanish");
    }

}

Test.java
package inheritance;

public class Test {

    public static void main(String[] args) {

        // هنا قمنا بإنشاء كائنات من البلدان الثلاثة
        Australia au = new Australia();
        Lebanon   lb = new Lebanon();
        Spain     sp = new Spain();

        // لعرض لغة كل بلد language() هنا قمنا باستدعاء الدالة
        au.language();
        lb.language();
        sp.language();

    }

}

•سنحصل على النتيجة التالية عند التشغيل.

English
Arabic
Spanish


أنت الآن فهمت لما قد تحتاج أن تفعل Override. سنقوم الآن بشرح طريقة عمل مترجم جافا عندما فعلت Override للدالة language().

إذا عدت للكلاس Lebanon, ستجد أننا فعلنا Override للدالة language(). إذاً هنا أصبح الكلاس Lebanon يملك دالتين إٍسمهما language(), الأولى هي التي ورثها من الـ Superclass, و الثانية هي التي قمنا بتعريفها عندما فعلنا Override.


بما أن الكلاس Lebanon يملك دالتين لهما نفس الإسم, النوع و عدد البارامتيرات, كيف عرف أي دالة يختار؟

عندما قمنا بتعريف الدالة language() من جديد في الـ Subclass, قام المترجم بإخفاء الدالة الأصلية ( أي دالة الـ Superclass ) و أظهر الدالة الجديدة فقط. بالإضافة إلى أن الكلمة @Override ضمنت لنا حصول هذا, لأنها تخبر المترجم أنه يوجد عدة دوال إسمهم language() لكننا نريد هذه الدالة بالتحديد عند استدعائها من هذا الكلاس.


إنتبه: في داخل الكلاس Lebanon, إذا أردنا إستخدام الدالة language() الموجودة في الـ Superclass و التي قام المترجم بإخفائها عندما قمنا بتعريفها من جديد, يمكننا ذلك بواسطة الكلمة super التي شرحناها في الدرس السابق.

شروط الـ Overriding للدوال
يجب أن يكون الـ Modifier المستخدم للدالة الجديدة هو نفسه المستخدم للدالة القديمة, و يجب أن يكون نوعه public أو protected.
عدد و نوع باراميترات الدالة الجديدة يجب أن يطابق عدد و نوع باراميترات الدالة القديمة.
نوع الإرجاع للدالة الجديدة يجب أن يكون نفس نوع الإرجاع للدالة القديمة.
الدالة المعرفة كـ private لا يمكن أن نفعل لها Override, لأن كلمة private تمنع إمكانية الوصول المباشر للدالة من الـ Subclass.
الدالة المعرفة كـ final لا يمكن أن نفعل لها Override, لأن كلمة final تمنع تغير محتوى الدالة بعد تعريفها.
الدالة المعرفة كـ static لا يمكن أن نفعل لها Override و لكن يمكن تعريفها من جديد في أي مكان, لأن كلمة static تجعل الدالة مشتركة بين جميع الكلاسات.
لا يمكن أن نفعل Override للكونستركتور.

Java
مفهوم الـ Overloading

مفهوم الـ Overloading
Overloading: تعني تعريف أكثر من دالة أو كونستركتور, لهم نفس الإسم, مختلفون في عدد أو نوع الباراميترات.
الفكرة من الـ Overloading, هي تجهيز عدة دوال لهم نفس الإسم, هذه الدوال تكون متشابهة من حيث الوظيفة, مختلفة قليلاً في الأداء.
فعلياً, تكون كل دالة تحتوي على ميزات إضافية عن الدالة التي أنشأت قبلها.

شروط الـ Overloading
الـ Overloading يطبق فقط على الدوال و الـ Constructors.
يجب أن يملكوا نفس الإسم.
يجب أن يختلفوا في نوع أو عدد الباراميترات.
نوع الإرجاع غير مهم, لا يستطيع المترجم التفريق بين الدوال إذا كانوا مختلفين في نوع الإرجاع.
أمثلة شاملة حول الـ Overloading

المثال الأول
تعريف دوال لها نفس الإسم و تختلف في نوع الباراميترات.

شاهد المثال »



المثال الثاني
تعريف دوال لها نفس الإسم و تختلف في عدد الباراميترات.

شاهد المثال »



المثال الثالث
تعريف دوال تعتمد على دوال موجودة قبلها في نفس الكلاس.

شاهد المثال »



المثال الرابع
تعريف دوال تعتمد على دوال موجودة في الـ Superclass.

شاهد المثال »



المثال الخامس
تعريف أكثر من كونستركتور في الكلاس.

شاهد المثال »


Java
تحويل الأنواع في جافا



مفهوم الـ Type Casting
Type Casting تعني تحويل نوع الكائن أو المتغير إلى نوع شبيه له. هذا التحويل يمكن تحقيقه فقط بين Superclass و Subclass.

بالنسبة لتحويل أنواع الكائنات. يوجد أسلوبين للتحويل و هما: Upcasting و Downcasting.

Upcasting تعني تحويل نوع كائن من الـ Subclass إلى نوع كائن من الـ Superclass.
Downcasting تعني تحويل نوع كائن من الـ Superclass إلى نوع كائن الـ Subclass.
التحويل في كلا الحالتين لا يقصد منه تحويل نوع الكائن الأصلي, بل تخصيص الطريقة التي سيتعامل فيها المترجم مع الكائن.


إذا قمت بإنشاء كلاس إسمه A, ثم قمت بإنشاء كلاس آخر إسمه B يرث منه. و بعدها فعلت DownCast لكائن من A على أنه B, هنا الكائن سيبقى كائن من A و لكن المترجم سيتعامل معه ككائن من B, و سيخفي خصائص الكلاس A فيه. سترى ذلك لاحقاً من خلال الأمثلة.


طريقة تحويل أنواع الكائنات
في الصورة التالية سترى كيف نفعل عمليات الـ Upcasting و الـ Downcasting.

downcasting and upcasting in java



تذكر دائماً أن الـ Upcasting هي التحويل من الأسفل إلى الأعلى, و الـ Downcasting هي التحويل من الأعلى إلى الأسفل كما في الصورة التالية:

downcasting and upcasting in java

Automatic Upcasting and Manual Upcasting
بالنسبة للـ Upcasting, يمكنك عدم تحديد نوع الـ Superclass المستخدم في التحويل لأن المترجم يفعل ذلك بشكل تلقائي.


مثال
•الطريقتين المذكورتين هنا مسموحتان في جافا و يعطيانك نفس النتيجة.

// Manual Upcasting    <-- الطريقة الأولى
A a = (A) new B();

// Automatic Upcasting <-- الطريقة الثانية
A a = new B();

الطريقة الأولى تسمى Manual Upcasting لأننا قمنا بذكر النوع الذي سيتم تحويل الكائن إليه.
الطريقة الثانية تسمى Automatic Upcasting لأننا لم نقم بذكر النوع الذي سيتم تحويل الكائن إليه, و بالتالي سيقوم المترجم بذلك بشكل تلقائي.
في الأمثلة التالية سنستخدم أسلوب الـ Automatic Upcasting و ليس الـ Manual Upcasting.

نقطة مهمة حول الـ Downcasting
بالنسبة للـ Downcasting, يجب تحديد نوع الـ Subclass المستخدم في التحويل لأن المترجم لا يفعل ذلك بشكل تلقائي, و السبب في ذلك أن التحويل من الأعلى إلى الأسفل بشكل عام يعني أن الكائن الذي تم إنشائه من الـ Superclass سيخسر بعض بياناته عند تحويله إلى كائن من الـ Subclass.

إذاً لا يمكن أن تفعل DownCast لكائن إلا إذا كنت قد سبق و فعلت له UpCast.


المثال الأول
A a = new B();
B b = a;           // Incompatible Type: A cannot be converted to B <-- سيظهر لك تحذير
•إذاً هنا كان يجب تحويل نوع الكائن a حتى تحل المشكلة.

A a = new B();
B b = (B) a;       // هنا حللنا المشكلة و أصبح الكود سليم لا يحتوي على أي خطأ


المثال الثاني
B b = new A();           // Incompatible Type: A cannot be converted to B <-- سيظهر لك تحذير
•إذاً هنا كان يجب تحويل نوع الكائن a حتى تحل المشكلة.

B b = (B) new A();       // هنا حللنا المشكلة و أصبح الكود سليم لا يحتوي على أي خطأ

أمثلة شاملة
سنقوم بتعريف كلاس إسمه Person عبارة عن شخص, بعدها سنقوم بتعريف كلاس إسمه Male و كلاس إسمه Female, و الإثنان يرثان من الكلاس Person. أي يجب ربط الكلاسات كما في الصورة التالية:

downcasting and upcasting in java


الآن سنقوم ببناء جميع هذه الكلاسات, ثم سنقوم ببناء الكلاس Test لتجربة مفهوم الـ Type Casting.
إنتبه: سنستخدم الدالة getClass() لمعرفة الكلاس الأصلي للكائنات.

المثال الأول
Person.java
package typecasting;

public class Person {

}

Male.java
package typecasting;

public class Male extends Person {

}

Female.java
package typecasting;

public class Female extends Person {

}

Test.java
package typecasting;

public class Test {

    public static void main(String[] args) {

        Female hala = new Female();           // hala إسمه Female هنا قمنا بتعريف كائن عادي من
        Male mhamad = new Male();             // mhamad إسمه Male هنا قمنا بتعريف كائن عادي من


                                              // Upcasting هنا سنجري عملية تحويل من النوع
        Person ziad = new Male();             // Male ثم حددنا أنه من النوع Person هنا قمنا بتعريف كائن من


                                              // Downcasting هنا سنجري عملية تحويل من النوع
        Female rola = new Female();           // rola إسمه Female هنا قمنا بتعريف كائن عادي من
        Person p    = rola;                   // Upcasting هذه العملية تسمى .rola ثم حددنا أنه يشير إلى الكائن Person هنا قمنا بتعريف كائن من
        Female rana = (Female) p;             // Downcasting هذه العملية تسمى .rola و الذي يشير في الأصل للكائن p للكائن DownCast هنا فعلنا


                                                         // هنا سنقوم بطباعة إسم الكلاس المشتق منه كل كائن قمنا بإنشائه
        System.out.println("1) "+ hala.getClass());      // Female كائن من الكلاس hala هنا ستخبرنا أن
        System.out.println("2) "+ mhamad.getClass());    // male كائن من الكلاس mhamad هنا ستخبرنا أن
        System.out.println("3) "+ ziad.getClass());      // male كائن من الكلاس ziad هنا ستخبرنا أن
        System.out.println("4) "+ rola.getClass());      // female كائن من الكلاس rola هنا ستخبرنا أن
        System.out.println("5) "+ p.getClass());         // Female و الذي نوعه rola لأنه أصلاً يشير إلى الكائن Female كائن من الكلاس p هنا ستخبرنا أن
        System.out.println("6) "+ rana.getClass());      // Female كائن من الكلاس rana هنا ستخبرنا أن

    }

}

•سنحصل على النتيجة التالية عند التشغيل.

1) class typecasting.Female
2) class typecasting.Male
3) class typecasting.Male
4) class typecasting.Female
5) class typecasting.Female
6) class typecasting.Female


حسناً, لقد فهمت الآن طريقة التحويل لكنك حتماً مرتبك بشأن نتيجة التشغيل, و لعلك تتساءل إذا كانت الكائنات rola, و p و rana كلها تشير إلى نفس الكائن؟!".

سنعلمك حيلة تجعلك تتأكد إذا كان يوجد أكثر من إسم يشير إلى نفس الكائن.
كل ما عليك أن تفعله هو طباعة عناوين الخانات التي تم تخزين الكائنات فيها في الذاكرة.

سنعيد نفس المثال السابق مع عرض عناوين الخانات التي تم تخزين الكائنات فيها في الذاكرة, و نلفت الإنتباه أن هذه النتيجة تتغير من حيث أسماء العناوين التي ستظهر كلما قمت بتشغيل البرنامج (ذاكرة الحاسوب تعمل هكذا), لكن الفكرة هنا أن المتغيرات التي تشير إلى نفس الكائن ستشير إليه نفسه في الذاكرة (أي ستشير إلى نفس العنوان في الذاكرة) و هذا ما يؤكد لنا أنهم يشيرون إليه.

المثال الثاني
Test.java
package typecasting;

public class Test {

    public static void main(String[] args) {

        Female hala = new Female();
        Male mhamad = new Male();


        Person ziad = new Male();


        Female rola = new Female();
        Person p    = rola;
        Female rana = (Female) p;


                                                         // هنا سنقوم بطباعة عنوان كل كائن تم إنشائه في الذاكرة
        System.out.println("1) "+ hala.toString());      // في الذاكرة hala هذا العنوان الذي يخزن الكائن
        System.out.println("2) "+ mhamad.toString());    // في الذاكرة mhamad هذا العنوان الذي يخزن الكائن
        System.out.println("3) "+ ziad.toString());      // في الذاكرة ziad هذا العنوان الذي يخزن الكائن
        System.out.println("4) "+ rola.toString());      // في الذاكرة rola هذا العنوان الذي يخزن الكائن
        System.out.println("5) "+ p.toString());         // في الذاكرة p هذا العنوان الذي يخزن الكائن
        System.out.println("6) "+ rana.toString());      // في الذاكرة rana هذا العنوان الذي يخزن الكائن

    }

}

•سنحصل على نتيجة تشبه النتيجة التالية عند التشغيل لأن الأماكن المحجوزة في الذاكرة لتخزين بيانات البرامج تتبدل كما قلنا سابقاً.

•قمنا بتعليم العناوين المشتركة باللون الأصفر.

1) typecasting.Female@60ec2ea8
2) typecasting.Male@31eb494e
3) typecasting.Male@4e19b97c
4) typecasting.Female@7ae0a3f2
5) typecasting.Female@7ae0a3f2
6) typecasting.Female@7ae0a3f2
•إذاً الكائنات الثلاثة rola, p و rana يشيرون فعلاً إلى نفس الكائن. و الكائن هنا هو كائن من الكلاس Female.



الفكرة الأخيرة التي عليك التأكد منها هي أنه عندما نقول أن الكائن نوعه كذا و لكنه بالتحديد من النوع كذا, فإن النوع الذي حددته له هو النوع الذي سيمثل الكائن.

مثال
Person ziad = new Male();
هنا سيعتبر المترجم أن الكائن ziad هو كائن من Male و ليس من Person.
و بالتالي سيحتوي الكائن ziad على نسخة من الأشياء الموجودة في Male و ليس الأشياء الموجودة في Person.



سنعيد نفس المثال السابق لكننا سنضيف دالة إسمها print() في الكلاس Person و سنفعل لها Override في الكلاسَين Male و Female.

المثال الثالث
Person.java
package typecasting;

public class Person {

    public void print() {
        System.out.println("I am a Person");
    }

}

Male.java
package typecasting;

public class Male extends Person {

    @Override
    public void print() {
        System.out.println("I am a Person and i'm a Male too.");
    }

}

Female.java
package typecasting;

public class Female extends Person {

    @Override
    public void print() {
        System.out.println("I am a Person and i'm a Female too.");
    }

}

Test.java
package typecasting;

public class Test {

    public static void main(String[] args) {

        Female hala = new Female();       // Female سيملك نسخة من أشياء الكلاس hala الكائن
        Male mhamad = new Male();         // Male سيملك نسخة من أشياء الكلاس hala الكائن


        Person ziad = new Male();         // Male سيملك نسخة من أشياء الكلاس ziad الكائن


        Female rola = new Female();       // Female سيملك نسخة من أشياء الكلاس rola الكائن
        Person p    = rola;               // Female و بالتالي نسخة من أشياء الكلاس rola سيملك نسخة من أشياء الكائن p الكائن
        Female rana = (Female) p;         // Female و الذي يعتبر في الأصل نسخة من p سيملك نسخة من أشياء الكائن rana الكائن


        hala.print();           // Female الموجودة في الكلاس print() هنا سيتم إستدعاء الدالة
        mhamad.print();         // Male الموجودة في الكلاس print() هنا سيتم إستدعاء الدالة
        ziad.print();           // Male الموجودة في الكلاس print() هنا سيتم إستدعاء الدالة
        rola.print();           // Female الموجودة في الكلاس print() هنا سيتم إستدعاء الدالة
        p.print();              // Female الموجودة في الكلاس print() هنا سيتم إستدعاء الدالة
        rana.print();           // Female الموجودة في الكلاس print() هنا سيتم إستدعاء الدالة

    }

}

•سنحصل على النتيجة التالية عند التشغيل.

I am a Person and i'm a Female too.
I am a Person and i'm a Male too.
I am a Person and i'm a Male too.
I am a Person and i'm a Female too.
I am a Person and i'm a Female too.
I am a Person and i'm a Female too.
تحويل أنواع البيانات البدائية
بالنسبة لأنواع البيانات البدائية (int, long, float, double, char) يمكنك أن تفعل لهم Cast عند الحاجة بكل سهولة.

مثال
Test.java
package typecasting;

public class Test {

    public static void main(String[] args) {

        double a = 10.55;

        // b و تخزينها في المتغير int إلى النوع double من النوع a هنا قمنا بتحويل نوع قيمة المتغير
        int b = (int)a;

        // أصلاً يقبل أعداد صحيحة double لأن النوع c مباشرةً في المتغير b هنا قمنا بتخزين قيمة المتغير
        double c = b;

        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.out.println("c = " + c);

    }

}

•سنحصل على النتيجة التالية عند التشغيل.

a = 10.55
b = 10
c = 10.0

إنتبه جيداً أثناء التحويل لأنك كما شاهدت في المثال السابق أنك قد تخسر بعض البيانات عند التحويل.

أخطاء محتملة قد تحدث عند التحويل
إذا لم تقم بتحويل الكائنات بطريقة صحيحة, سيؤدي ذلك إلى ظهور أخطاء في الكود, قمنا بتقسيمها إلى ثلاثة إحتمالات:

خطأ من النوع RuntimeException و هذا الخطأ يظهر لك قبل تشغيل البرنامج.
خطأ من النوع ClassCastException و هذا الخطأ يظهر لك أثناء تشغيل البرنامج.

وضعنا هنا أمثلة حول أنواع الأخطاء التي قد تحدث عند تحويل أنواع الكائنات.


Java
التجريد في جافا


مفهوم الـ Abstraction
تجريد: تعني Abstraction في اللغة الإنجليزية, و هو أسلوب مهم جداً يستخدم لتسهيل كتابة الأوامر على المبرمجين, فهو يجعلك قادراً على تنفيذ ما تريد دون الحاجة إلى معرفة كافة التفاصيل التي تم فيها تنفيذ ذلك. إذاً الـ Abstraction يجعلك تتعامل مع الأشياء بسطحية بدل أن تغوص في معرفة تفاصيل الكودات المعقدة.

فمثلاً إذا كنت تنوي بناء برنامج يتيح لمستخدميه إرسال إقتراحات حول التطبيق من خلال البريد الإلكتروني, في هذه الحالة لن يكون عليك القلق بتاتاً حول طريقة تعامل أوامر جافا مع البروتوكولات التي تعمل عندما يتم إرسال رسائل إلكترونية من خلال هذا التطبيق, لأنك لست مسؤولاً عنها, فعلياً هذه وظيفة شركة Sun التي تقوم بتطوير لغة جافا. و هم يخبرونك أنه لإرسال بريد إلكتروني إستخدم هذه الأوامر البسيطة فقط.

إذاً الـ Abstraction هو أسلوب يستخدم لإخفاء تفاصيل تنفيذ البرنامج. لتطبيق مفهوم الـ Abstraction نستخدم الكلمة abstract ضمن شروط محددة.


مصطلحات تقنية
إذا أردت تعريف الشيء كـ abstract, أكتب فقط الكلمة abstract قبله.

الكلاس المعرف كـ abstract يسمى Abstract Class.
الكلاس العادي الغير معرف كـ abstract يسمى Concrete Class.
الدالة المعرفة كـ abstract تسمى Abstract Method أو Abstract Function.
Abstract Class
إذا وضعت الكلمة abstract قبل إسم الكلاس, ستتغير قليلاً طريقة التعامل معه لأنه لم يعد كلاس عادي. و عندها سيراه المترجم كـ Abstract Class.


نقاط مهمة حول الـ Abstract Class
الكلاس العادي لا يمكنه أن يحتوي على دوال نوعها abstract.
الـ Abstract Class يمكنه أن يحتوي على دوال عادية, و يمكنه أن يحتوي على دوال نوعها abstract.
إذا قمت بتعريف الكلاس كـ abstract, فهذا يعني أن هذا الكلاس لا يمكن إنشاء كائنات منه.
بما أنه لا يمكن إنشاء كائنات من Abstract Class, فهذا يعني أنه للإستفادة من هذا الكلاس, يجب وراثته.
الكلاس الذي يرث من كلاس نوعه abstract, يجب أن يفعل Override لجميع الدوال المعرفة كـ abstract.


طريقة تعريف كلاس نوعه abstract
نكتب فقط الكلمة abstract قبل الكلمة class.

مثال
abstract class Example {

}
Abstract Method
إذا كنت تريد بناء دالة و جعل الكلاس الذي يرثها هو المسؤول عن كتابتة محتواها, قم بتعريفها كـ abstract.


نقاط مهمة حول الـ Abstract Method
إذا وضعت الكلمة abstract قبل إسم الدالة, فهذا يعني أنها دالة من النوع abstract.
الدالة التي نوعها abstract هي دالة لها إسم و نوع محدد, لكنها لا تحتوي على body (جسم), أي لا تملك أقواس بداية و نهاية { }
الدالة العادية تحتوي على أقواس البداية و النهاية { }
الـ Abstract Method يجب وضع فاصلة منقوطة ; في آخرها بدل أقواس البداية و النهاية.
الكلاس الذي يرث من كلاس نوعه abstract, يجب أن يفعل Override لكل دوالة نوعها abstract, أي يجب أن يكتب الـ body لهذه الدوال.


طريقة تعريف دوال نوعها abstract
نكتب فقط الكلمة abstract بعد Modifier الدالة.

أمثلة
public abstract void print();

public abstract int getId();

public abstract void setId(int id);


إنتبه: عندما تفعل Override لدالة نوعها abstract, يجب أن لا تعرفها كـ abstract من جديد.

مثال
•لنفترض أننا قمنا بتعريف دالة إسمها displayMessage() كـ abstract

public abstract void displayMessage();

•لا يجب أن نكتب abstract عندما تفعل لها Override.

@Override
public abstract void displayMessage() {       // abstract methods cannot have a body <-- abstract ستظهر لك هذا التنبيه بسبب الكلمة

}

•لتصحيح هذا الخطأ, نمسح فقط الكلمة abstract.

@Override
public void displayMessage() {               // نلاحظ أن التنبيه إختفى

}
أمثلة شاملة
وضعنا هنا ثلاثة أمثلة بسيطة و مترابطة تعلمك الأساسيات.

شاهد الأمثلة »


وضعنا هنا مثال بسيط حول Abstract Class يرث من Abstract Class.

شاهد المثال »


وضعنا هنا مثال مهم يعلمك متى تحتاج إلى إنشاء Abstract Class.

شاهد المثال »


سترى فائدة الـ Abstraction أيضاً في دروس متقدمة عندما تستخدم كلاسات جاهزة تتيح لك التعامل مع الشبكات (Networks), الواجهات (GUI) و قواعد البيانات (DataBases) بكل سهولة. كما أنك ستراها عندما تعمل على بناء مشاريع كبيرة, تجبرك على إستخدام هذا الأسلوب لتسهيل العمل في المشروع.


Java
الإنترفيس في جافا


مفهوم الإنترفيس
interface هي كلمة محجوزة في جافا, و تكتب إنترفيس في اللغة العربية.

في دروس سابقة رأينا أنه في جافا لا يمكن للكلاس العادي أن يرث من أكثر من كلاس في نفس الوقت لأنه يمكن للكلاس أن يفعل extends لكلاس واحد فقط.
من أجل ذلك عمل مطوروا لغة جافا على إبتكار نوع جديد يشبه الكلاس العادي و يسمح لنا بتطبيق مبدأ تعدد الوراثة الذي يعتبر شيء مهم جداً في لغات البرمجة. هذا النوع الجديد يسمى interface.

يستخدم الإنترفيس كوسيط لتطوير البرامج, و المقصود هنا أن المبرمج يستخدم الإنترفيس إذا أراد جعل برنامجه قابلاً للتطوير بكل مرونة سواءً لأجل نفسه أو من قبل مبرمجين آخرين, فهو يستخدم الإنترفيس لإجبار المبرمجين على اتباع أسلوب محدد يضمن توافق الكودات التي سيكتبها المطورون الآخرون مع كودات البرنامج الأساسية.

إذاً الإنترفيس يستخدم لتجهيز أسلوب يمكن السير عليه لربط الكلاسات بأسلوب سهل, منطقي و مفهوم. سترى قوة الإنترفيس الحقيقية في درس الـ Polymorphism لكننا سنركز في هذا الدرس على طريقة التعامل مع الإنترفيس في جافا.

التعامل مع الإنترفيس
التعامل مع الإنترفيس يختلف عن التعامل مع الكلاس العادي, و هو يشبه التعامل مع الـ Abstract Class.
فعلياً الإنترفيس يطبق مبدأ الـ Full Abstraction.


الأشياء التي يمكن تعريفها بداخل الإنترفيس
دوال لا تملك body, أي Abstract Method.
متغيرات مع إعطائهم قيمة بشكل مباشرةً عند تعريفهم. لأن أي متغير تقوم بتعرفه بداخل الإنترفيس يعتبر معرف كـ public final static بشكل تلقائي.
Nested Classes, أي كلاس نوعه static بداخل كلاس نوعه static.
Nested Interfaces, أي إنترفيس بداخل إنترفيس.

طريقة تعريف الإنترفيس
الإنترفيس هو في الأساس Full Abstract, لكن لا يجب وضع الكلمة abstract عند تعريفه. و لا يمكن تعريف إنترفيس كـ prviate أو protected لأنه دائماً يعتبر public حتى لو لم تضع كلمة public قبله. كما أنه لا يمكن تعريف الإنترفيس كـ final أو static لأنه تم تصميم الإنترفيس لجعل أي كلاس يرثه يفعل Override للدوال الموجودة فيه.

إذاً لتعريف إنترفيس, أكتب interface ثم ضع له أي إسم تريده.

مثال
interface MyInterface {

}


طريقة تعريف متغيرات في الإنترفيس
متغيرات الإنترفيس تعتبر public final static حتى لو لم تقم بتعريفها كذلك, الأمر الذي يجعلك مجبراً على إعطاءها قيمة مباشرةً عند تعريفها مع عدم إمكانية تغيير هذه القيمة.

مثال
interface MyInterface {

    int X = 1;           // حتى و لم نعرفه كذلك public final static يعتبر معرف كـ X المتغير

}


طريقة تعريف دوال في الإنترفيس
دوال الإنترفيس هي في الأساس Abstract Method, إذاً لا حاجة إلى وضع الكلمة abstract عند تعريف أي دالة بداخله. كما أن أي دالة يتم تعريفها بداخله تعتبر public حتى لو لم تعرفها كـ public. و لا يمكن تعريف الدوال فيه كـ prviate أو protected أو final أو static.

مثال
interface MyInterface {

    void myMethod();           // حتى و لم نعرفها كذلك public abstract تعتبر معرفة كـ myMethod() الدالة
    long myResult();           // حتى و لم نعرفها كذلك public abstract تعتبر معرفة كـ myResult() الدالة

}


شروط الربط بين الكلاس و الإنترفيس
لا يمكن إنشاء كائن من إنترفيس.
يستطيع الكلاس أن يرث من كلاس واحد, أي يستطيع أن يفعل extends لكلاس واحد.
لا يستطيع الكلاس أن يرث من إنترفيس, أي لا يستطيع أن يفعل extends لإنترفيس.
يستطيع الكلاس تنفيذ إنترفيس أو أكثر, أي يستطيع أن يفعل implements لإنترفيس أو أكثر.
الكلاس الذي ينفذ إنترفيس, يجب أن يفعل Override لجميع الدوال التي ورثها من هذا الإنترفيس .
يستطيع الإنترفيس أن يرث من إنترفيس أو أكثر. أي يستطيع الإنترفيس أن يفعل extends لإنترفيس أو أكثر.


تنفيذ الإنترفيس
عند تنفيذ أي إنترفيس, يجب أن تفعل Override لجميع الدوال الموجودة فيه, و يجب تعريفهم كـ public حتى يستطيع أي كائن من هذا الكلاس أن يستخدمهم.

مثال
A.java
interface A {               // لجميع الدوال الموجودة فيه Override أي كلاس ينفذ هذا الإنترفيس يجب أن يفعل

    void print();

}

B.java
class B implements A {      // A ينفذ الإنترفيس B هنا قلنا أن الكلاس

    @Override               // public لا تنسى إضافة كلمة .print() للدالة Override مجبور أن يفعل B الكلاس
    public void print() {
        System.out.println("B should Override this method");
    }

}


شروط أساسية عليك اتباعها عند إنشاء إنترفيس
لا تستخدم أي Access Modifer عند تعريف إنترفيس.
لا تستخدم أي Access Modifer عند تعريف دالة بداخل إنترفيس.
بداخل الإنترفيس جميع الدوال يجب أن لا تملك body. و يمكن جعل الدالة ترمي إستثناء.
لا يمكن للإنترفيس أن يملك كونستركتور.
شاهد المثال »




الأسباب التي تدفعك إلى استخدام الإنترفيس
إذا كنت تنوي بناء كلاس يطبق مفهوم الـ Full Abstraction.
لحل مشكلة تعدد الوراثة.
لتطبيق مبدأ تعدد الأشكال Polymorphism.
الفرق بين الكلمتين extends و implements
extends تعني وراثة, نستخدمها لجعل كلاس يرث من كلاس, أو لجعل إنترفيس يرث من إنترفيس أو أكثر.
implements تعني تنفيذ, نستخدمها لجعل كلاس ينفذ إنترفيس أو أكثر.

الصورة التالية توضح لك متى يمكن إستخدام الكلمتين extends و implements.

الوراثة من أكثر من كلاس في جافا


متى تكون مجبراً على أن تفعل Override و متى لا
في حالة الـ extends:

إذا كان عندك كلاس يرث من كلاس, يكون للـ Subclass الحرية في أن يفعل Override للدوال التي ورثها.
إذا كان عندك كلاس يرث من كلاس نوعه abstract, يكون الـ Subclass مجبراً على أن يفعل Override للدوال المعرفة كـ abstract.
إذا كان عندك إنترفيس يرث من إنترفيس, لا يستطيع الـ SubInterface أن يفعل Override للدوال التي ورثها.
في حالة الـ implements:

إذا كان عندك كلاس ينفذ إنترفيس, يكون الـ Subclass مجبراً على أن يفعل Override للدوال التي ورثها.
إذا كان عندك كلاس نوعه abstract ينفذ إنترفيس, يكون الـ Subclass له الحرية في أن يفعل Override للدوال التي ورثها.
أشكال تعدد الوراثة في جافا
إذا قام كلاس بتنفيذ أكثر من إنترفيس, أو إذا قام إنترفيس بوراثة أكثر من إنترفيس, تسمى هذه العمليات وراثة متعددة (Multiple Inheritance).

الصورة التالية توضح لك شكل الوراثة المتعددة.

الوراثة من أكثر من كلاس في جافا



ملاحظة
إذا كان الكلاس ينفذ أكثر من إنترفيس, يجب وضع فاصلة بينهم.
في حال كان الكلاس يرث من كلاس آخر و ينفذ إنترفيس أو أكثر, إفعل extends للكلاس في البداية ثم إفعل implements لأي إنترفيس تريد.


المثال الأول
•هنا قمنا بتعريف إثنين إنترفيس A و B, و قمنا بتعريف كلاس إسمه C و قلنا أن C ينفذ A و B.

interface A { }

interface B { }

class C implements A, B { }     // لكل دالة ورثها منهما Override بمعنى أنه يجب أن يفعل B و الإنترفيس A ينفذ الإنترفيس C هنا الكلاس


المثال الثاني
•هنا قمنا بتعريف ثلاثة إنترفيس A ,B و C, و قلنا أن C يرث من A و B.

interface A { }

interface B { }

interface C extends A, B { }     // B و الإنترفيس A يرث من الإنترفيس C هنا الإنترفيس


المثال الثالث
•هنا قمنا بتعريف إنترفيس A, كلاس B و كلاس C يرث من B و ينفذ A.

interface A { }

class B { }

class C extends B implements A { }     // A و ينفذ الإنترفيس B يرث من الكلاس C هنا الكلاس

في حال واجهت صعوبة في فهم الأمثلة السابقة, قمنا بتوضيحها بتفصيل هنا.

شاهد الأمثلة »

Nested Interfaces
يمكنك تعريف إنترفيس بداخل إنترفيس بداخل إنترفيس إلخ.. ويمكنك تنفيذ الإنترفيس الذي تريده منهم بالتحديد متى شئت.

المثال الأول
•هنا قمنا بتعريف إنترفيس A, و بداخله إنترفيس B, و بداخله إنترفيس C
ثم قمنا بتعريف كلاس D ينفذ A, و كلاس E ينفذ B, و كلاس F ينفذ C

interface A {                      // A       نكتب A لنصل للإنترفيس
    interface B {                  // A.B     نكتب A الموجود بداخل الإنترفيس B لنصل للإنترفيس
        interface C {              // A.B.C   نكتب A الموجود بداخل الإنترفيس B الموجود بداخل الإنترفيس C لنصل للإنترفيس
        }
    }
}

class D implements A { }           // A ينفذ الإنترفيس D هنا الكلاس

class E implements A.B { }         // B ينفذ الإنترفيس E هنا الكلاس

class F implements A.B.C { }       // C ينفذ الإنترفيس F هنا الكلاس

يمكنك أن تفعل import للإنترفيس و عندها يمكنك أن تكتب إسمه فقط للوصول إليه.

المثال الثاني
•هنا سنفعل import للإنترفيس B و import للإنترفيس C بدل أن نصل إليهم من الإنترفيس A.

package interfaces;

import interfaces.A.B;         // لذلك أصبح يمكننا الوصول إليه مباشرةً B هنا قمنا بتحديد المكان الموجود فيه الإنترفيس
import interfaces.A.B.C;       // لذلك أصبح يمكننا الوصول إليه مباشرةً C هنا قمنا بتحديد المكان الموجود فيه الإنترفيس

interface A {                  // A نكتب A لنصل للإنترفيس
    interface B {              // B نكتب A الموجود بداخل الإنترفيس B لنصل للإنترفيس
        interface C {          // C نكتب A الموجود بداخل الإنترفيس B الموجود بداخل الإنترفيس C لنصل للإنترفيس
        }
    }
}

class D implements A { }       // A ينفذ الإنترفيس D هنا الكلاس

class E implements B { }       // B ينفذ الإنترفيس E هنا الكلاس

class F implements C { }       // C ينفذ الإنترفيس F هنا الكلاس

إنتبه: هنا الإنترفيس B لا يعتبر أنه يرث من الإنترفيس A. كما أن الإنترفيس C لا يعتبر أنه يرث من الإنترفيس B.

Tagged Interfaces
Tagged Interface يقال لها أيضاً Marker Interface, فكرتها تقسيم الـ Interfaces المتشابهين في مجموعات.
لتقسيم الـ Interfaces إلى مجموعات ننشئ إنترفيس فارغ, ثم نجعل كل إنترفيس يشبهه من حيث الفكرة يرث منه.

الآن لنفترض أننا نريد إنشاء مجموعة خاصة للألوان. أول شيء نفعله هو إنشاء إنترفيس فارغ خاص للألوان إسمه Colors.
بعدها نجعل كل إنترفيس له علاقة بالألوان يرث منه, و هكذا سيعتبر المترجم أن كل إنترفيس يرث من Colors هو من النوع Colors.


مثال
interface Colors { }

interface RGB implements Colors { }       // Colors أي ينتمي إلى المجموعة Colors يرث من الإنترفيس RGB الإنترفيس

interface PMS implements Colors { }       // Colors أي ينتمي إلى المجموعة Colors يرث من الإنترفيس PMS الإنترفيس

interface HEX implements Colors { }       // Colors أي ينتمي إلى المجموعة Colors يرث من الإنترفيس HEX الإنترفيس

interface HSL implements Colors { }       // Colors أي ينتمي إلى المجموعة Colors يرث من الإنترفيس HSL الإنترفيس

سترى فائدة الـ Tagged Interface في الدرس التالي.

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


مفهوم الـ Polymorphism
Polymorphism تكتب بوليمورفيزم في اللغة العربية, و تعني تعدد الأشكال.
المقصود من البوليمورفيزم هو بناء دالة تنفذ أوامر مختلفة على حسب الكائن الذي يمرر لها كـ argument.
بوليمورفيزم تعني قدرة الكائن على أخذ عدة أشكال, تحديداً قدرة الكائن على التحول إلى نوع مشتق منه.


تحقيق البوليمورفيزم
البوليمورفيزم هو مجرد أسلوب في كتابة الكود و يمكنك تحقيقيه بعدة أشكال مختلفة تجعلك تصل لفكرة تعدد الكائنات.
عملية البوليمورفيزم تبنى في العادة بداخل الدوال, حيث أننا نقوم ببناء دالة تأخذ باراميتر عبارة عن كلاس أو إنترفيس.
عند إستدعاء هذه الدالة, الكائن الذي نمرره لها كـ argument مكان هذا الباراميتر هو الذي يحدد طريقة عملها.

إذاً في البوليمورفيزم نستخدم مبدأ الـ Upcasting حيث أن الكائن الذي سيتم تمريره إلى الدالة يجب أن يحتوي على الدوال و الخصائص المشتركة بين الـ Superclass و الـ Subclass.
و عند إستدعاء أي دالة من الكائن الذي تم تمريره, ستكون هذه الدالة موجودة في الـ Superclass و الـ Subclass و لكنها ستتنفذ على أساس الـ Subclass.

لا تقلق ستفهم المقصود من الأمثلة.

Polymorphic Array
في دروس سابقة رأيت أنه يمكنك تعريف مصفوفة من أي نوع تريده, فمثلاً يمكنك تعريف مصفوفة من النوع int, double أو String إلخ..

في حال قمت بتعريف مصفوفة نوعها int, عندها يمكنك تخزين قيمة نوعها int في كل عنصر موجود فيها.
في حال قمت بتعريف مصفوفة نوعها String, عندها يمكنك تخزين قيمة نوعها String في كل عنصر موجود فيها.
خلاصة: في جافا, يمكنك تعريف مصفوفة من أي نوع تريده.

Polymorphic Array: تعني تطبيق مبدأ البوليمورفيزم من خلال مصفوفة.
و المقصود هنا أنه يمكنك تعريف مصفوفة نوعها كائن و تخزين كائنات من نفس نوعها فيها.


في المثال التالي قمنا بإنشاء كلاس إسمه A يحتوي على متغير إسمه x و دالة إسمها printX().
بعدها قمنا بإنشاء كلاس آخر إسمه Test.
بداخل الكلاس Test قمنا بإنشاء 5 كائنات من الكلاس A, ثم قمنا بتعريف مصفوفة إسمها list نوعها a و وضعنا فيها الكائنات التي أنشأناها من الكلاس A.

كل كائن وضعناه فيها يعتبر عنصر من عناصرها, و بالتالي يمكننا الوصول لهم أيضاً منها من خلال أرقام الـ index إن أردنا.
مثال: list[0], list[1], list[2] إلخ..

المثال الأول
A.java
class A {           // A هنا قمنا بتعريف كلاس إسمه

  public int x;

    public void printX(){
        System.out.println("x contain: " + x);
    }

}

Test.java
public class Test {

    public static void main(String[] args) {

        // A أي 5 كائنات نوعهم ,A هنا قمنا بتعريف 5 كائنات من الكلاس
        A a1 = new A();
        A a2 = new A();
        A a3 = new A();
        A a4 = new A();
        A a5 = new A();

        // فيها A ثم قمنا بتخزين جميع الكائنات المشتقة من ,A هنا قمنا بإنشاء مصفوفة نوعها
        A[] list = { a1, a2, a3, a4, a5 };

        // list المخزن كثالث عنصر في المصفوفة a3 الموجود في الكائن x هنا قمنا بإعطاء قيمة للمتغير
        list[2].x = 14;

        // list[2] الموجودة في العنصر x سترجع قيمة المتغير printX() لاحظ أن الدالة
        a3.printX();

        // a3 الموجودة في العنصر x سترجع قيمة المتغير printX() و لاحظ أن الدالة
        list[2].printX();

    }

}

•سنحصل على النتيجة التالية عند التشغيل.

x contain: 14
x contain: 14


إذاً يمكنك بناء مصفوفة نوعها كلاس, و تخزين كائنات من نفس النوع فيها كما رأيت في المثال السابق.
في المثال التالي سترى أنه يمكنك تخزين كائنات مشتقة من نفس نوع المصفوفة فيها أيضاً و هذا من مبادئ الـ Upcasting و تطبيق لمبدأ الـ Polymorphic Array.


في المثال التالي قمنا بإنشاء كلاس إسمه A و نوعه abstract, و يحتوي على دالة إسمها print() و نوعها abstract أيضاً.
بعدها قمنا بإنشاء إثنين كلاس B و C يرثان من A. بعدها قمنا بإنشاء كلاس آخر إسمه Test و الذي سنطبق فيه مبدأ الـ Polymorphic Array.
بداخل الكلاس Test قمنا بإنشاء كائن من الكلاس B و كائن من الكلاس C, ثم قمنا بتعريف مصفوفة إسمها list و نوعها a, و وضعنا فيها الكائنات التي ترث من الكلاس A.

المثال الثاني
A.java
package polymorphism;

public abstract class A {                   // إذاً لا يمكن إنشاء كائنات منه ,Abstract نوعه A الكلاس

    public abstract void print();           // abstract لهذه الدالة لأن نوعها Override يجب أن يفعل A أي كلاس سيرث من الكلاس

}

B.java
package polymorphism;

public class B extends A {                 // A يرث من الكلاس B هنا قلنا أن الكلاس

    @Override
    public void print() {
        System.out.println("class B");     // إذا قمت باستدعاء هذه الدالة من هذا الكلاس ستعرض هذه الجملة
    }

}

C.java
package polymorphism;

public class C extends A {                 // A يرث من الكلاس C هنا قلنا أن الكلاس

    @Override
    public void print() {
        System.out.println("class C");     // إذا قمت باستدعاء هذه الدالة من هذا الكلاس ستعرض هذه الجملة
    }

}

Test.java
package polymorphism;

public class Test {

    public static void main(String[] args) {

        A b = new B();                         // B ثم قمنا بتحديد نوعه ككائن من A هنا قمنا بإنشاء كائن من الكلاس
        A c = new C();                         // C ثم قمنا بتحديد نوعه ككائن من A هنا قمنا بإنشاء كائن من الكلاس

        A[] list = new A[2];                   // تتألف من عنصرين فقط A هنا قمنا بإنشاء مصفوفة نوعها

                                               // فيها A سنخزن الكائنات المشتقة من الكلاس
        list[0] = b;                           // list[0] في أول عنصر في المصفوفة b هنا قمنا بتخزين الكائن
        list[1] = c;                           // list[1] في ثاني عنصر في المصفوفة c هنا قمنا بتخزين الكائن

        for(int i=0; i<list.length; i++) {     // هنا قمنا ببناء حلقة للمرور على جميع عناصر المصفوفة
            list[i].print();                   // الخاصة بكل عنصر print() هنا سيتم إستدعاء الدالة
        }                                      // على إختلاف كل كائن موجود في المصفوفة print() إذاً هنا سيختلف أداء الدالة

    }

}

•سنحصل على النتيجة التالية عند التشغيل.

class B
class C
Polymorphic Argument
Polymorphic Argument تعني بناء دالة تنفذ أوامر مختلفة على حسب الكائن الذي يمرر لها كـ argument.


الآن سنقوم بتعريف كلاس إسمه Shape و نوعه abstract, هذا الكلاس سنعتبره كلاس أساسي لجميع الأشكال الهندسية, أي سيكون Superclass لأي كلاس يمثل أحد الأشكال الهندسية. Shape يحتوي على دالة إسمها shapeForm() و نوعها abstract أيضاً, هذه الدالة فكرتها طباعة الشكل الهندسي للكائن الذي يقوم باستدعائها.

بعدها سنقوم بتعريف أربع كلاسات Square, Rectangle, Triangle و Circle.
كل كلاس منهم سيمثل شكل هندسي معين, إذاً يجب أن يرثوا جميعاً من الكلاس Shape, و يجب أن يفعلوا Override للدالة shapeForm() بهدف جعلها ترسم الشكل المطلوب من كل كلاس.

بعدها سنقوم بتعريف كلاس إسمه Drawer و الفكرة منه بناء كلاس لرسم أي كائن مشتق من Shape.

بعد إنشاء جميع هذه الكلاسات, سنقوم بإنشاء الكلاس Test لتجربة الكود.

مثال
Shape.java
package polymorphism;

public abstract class Shape {

    // لها حتى تطبع الشكل المناسب له Override على كل كائن يمثل شكل هندسي أن يفعل
    public abstract void shapeForm();

}

Square.java
package polymorphism;

public class Square extends Shape {

    // إذا قمت باستدعاء هذه الدالة من هذا الكائن, ستعرض لك شكل يشبه المربع
    @Override
    public void shapeForm() {
        System.out.println("* * * *\n* * * *\n* * * *\n");
    }

}

Rectangle.java
package polymorphism;

public class Rectangle extends Shape {

    // إذا قمت باستدعاء هذه الدالة من هذا الكائن, ستعرض لك شكل يشبه المربع
    @Override
    public void shapeForm() {
        System.out.println("* * * * * *\n* * * * * *\n* * * * * *\n");
    }

}

Triangle.java
package polymorphism;

public class Triangle extends Shape {

    // إذا قمت باستدعاء هذه الدالة من هذا الكائن, ستعرض لك شكل يشبه المثلث
    @Override
    public void shapeForm() {
        System.out.println("    *\n   * *\n  * * *\n * * * *\n* * * * *\n");
    }

}

Circle.java
package polymorphism;

public class Circle extends Shape {

    // إذا قمت باستدعاء هذه الدالة من هذا الكائن, ستعرض لك شكل يشبه الدائرة
    @Override
    public void shapeForm() {
        System.out.println("  * * *\n* * * * *\n* * * * *\n  * * *\n");
    }

}

Drawer.java
package polymorphism;

public class Drawer {

    public void draw(Shape s) {       // Shape عند إستدعاء هذه الدالة يجب ان نمرر لها أي كائن مشتق من الكلاس
        s.shapeForm();                // الموجودة في الكائن الذي قام باستدعاءها shapeForm() هنا سيتم استدعاء الدالة
    }

}

Test.java
package polymorphism;

public class Test {

    public static void main(String[] args) {

                                          // هنا كل كائن سيمثل شكل هندسي معين <-- Shape هنا قمنا بإنشاء 4 كائنات من الكلاس
        Shape s = new Square();           // Square و حددنا أن نوعه Shape هنا قمنا بإنشاء كائن من
        Shape r = new Rectangle();        // Rectangle و حددنا أن نوعه Shape هنا قمنا بإنشاء كائن من
        Shape t = new Triangle();         // Triangle و حددنا أن نوعه Shape هنا قمنا بإنشاء كائن من
        Shape c = new Circle();           // Circle و حددنا أن نوعه Shape هنا قمنا بإنشاء كائن من

        Drawer drawer = new Drawer();     // الموجودة فيه draw() حتى نستطيع استخدام الدالة drawer هنا قمنا بإنشاء كائن من

                                          // لعرض شكل كل كائن draw() هنا قمنا باستدعاء الدالة
        drawer.draw(s);                   // Square المعرفة في الكلاس shapeForm() سيتم إستدعاء الدالة ,Square نوعه s بما أن الكائن
        drawer.draw(r);                   // Rectangle المعرفة في الكلاس shapeForm() سيتم إستدعاء الدالة ,Rectangle نوعه r بما أن الكائن
        drawer.draw(t);                   // Triangle المعرفة في الكلاس shapeForm() سيتم إستدعاء الدالة ,Triangle نوعه t بما أن الكائن
        drawer.draw(c);                   // Circle المعرفة في الكلاس shapeForm() سيتم إستدعاء الدالة ,Circle نوعه c بما أن الكائن

    }

}

•سنحصل على النتيجة التالية عند التشغيل.

* * * *
* * * *
* * * *

* * * * * *
* * * * * *
* * * * * *

    *
   * *
  * * *
 * * * *
* * * * *

  * * *
* * * * *
* * * * *
  * * *
العامل instanceof في Polymorphism
العامل instanceof يستخدم لمعرفة إذا كان الكائن مشتقاً من كلاس معين أم لا.
في حال كان مشتقاً منه فإنه يرجع true, أما إذا لم يكن مشتقاً منه فإنه يرجع false.


في المثال التالي قمنا بإنشاء كلاس إسمه A.
بعدها قمنا بإنشاء إثنين كلاس B و C يرثان من A.
بعدها قمنا بإنشاء كلاس آخر إسمه Test.
بداخل الكلاس Test قمنا بتعريف دالة إسمها check() تأخذ كائن مشتق من A كـ argument فتعرض لنا إسم الكلاس الذي إشتق منه الكائن.
بعدها قمنا بإنشاء كائن من الكلاس B و كائن من الكلاس C, ثم قمنا بتمريرهما في الدالة check().

مثال
Person.java
package polymorphism;

public class A {

}

B.java
package polymorphism;

public class B extends A {

}

C.java
package polymorphism;

public class C extends A {

}

Test.java
package polymorphism;

public class Test {

    public static void main(String[] args) {

        A b = new B();                 // B و حددنا أن نوعه A هنا قمنا بإنشاء كائن من
        A c = new C();                 // C و حددنا أن نوعه A هنا قمنا بإنشاء كائن من

        check(b);                      // لمعرفة إسم الكلاس المشتق منه check() في الدالة b هنا قمنا بتمرير الكائن
        check(c);                      // لمعرفة إسم الكلاس المشتق منه check() في الدالة c هنا قمنا بتمرير الكائن

    }


    // سنستخدمها لمعرفة إسم الكلاس الذي إشتق منه كل كائن static هنا قمنا بتعريف دالة نوعها
    static void check (A obj) {
        if(obj instanceof B)            // سيتم تنفيذ أمر الطباعة الموجود فيها B كائن من الكلاس obj في حال كان الـ
        {
            System.out.println("This is an object from the class B");
        }
        else if(obj instanceof C)       // سيتم تنفيذ أمر الطباعة الموجود فيها C كائن من الكلاس obj في حال كان الـ
        {
            System.out.println("This is an object from the class C");
        }
    }

}

•سنحصل على النتيجة التالية عند التشغيل.

This is an object from the class B
This is an object from the class C


إذاً هناك طرق كثيرة لتحقيق مبدأ تعدد الأشكال و قد تعلمنا بعضها في هذا الدرس.







تعليقات