مفهوم الوراثة inheritance
الوراثة ( inheritance ) كما هو الحال المتعارف عليه في أرض الواقع أن يَرِث الإبن من أبيه بعض الصفات او من أمه بعض الصفات.
نفس الأمر لدينا في البرمجة الكائنية object oriented programming في مفهوم الوراثة و هو أن يرث كلاس من كلاس آخر بحيث أن الكلاس الوارث يسمى الإبن و الكلاس المورث يسمى الأب. بحيث يستطيع الإبن الوصول لكافة الدوال و المتغيرات الموجودة في الكلاس الأب و إعادة إستخدامها مرة أخرى ما عدا المتغيرات المحمية الخاصة بالكلاس الأب.
نستفيد من الوراثه هي أن نختصر الكود فبدلاً من إعادة بناء الدالة مرة أُخرى إذا كانت الدالة موجودة في الكلاس الأب.
نستطيع إعادة إستخدامها مرة أُخرى بدلاً من بناءها و إستهلاك الذاكرة و إطالة الكود بحيث يكون البرنامج منظم و اجمل.
إذاً نحن قلنا بأن الكلاس الذي يرث إسمه الكلاس الإبن و الكلاس الذي يورّث إسمه الكلاس الأب. يستطيع الإبن الوصول لكافة متغيرات و دوال الكلاس الأب ما عدا المتغيرات المحمية, سنرى الآن كيف نطبق عملية الوراثة inheritance في الجافا.
لنفترض أنه لدينا كلاس إسمه c و كلاس اسمه A و لجعل الكلاس A يرث من الكلاس c نستخدم الكلمة المحجوزة extends يعني أن A يرث من c و يعني أصبح A هو الإبن و ال c هو الأب. الآن يستطيع الكلاس A أن يصل لجميع المتغيرات و الدوال الذي في أبيه, و يكون كالتالي.
package a; class c { # } public class A extends c { public static void main(String []args){ System.out.println("Hello World"); } }
في هذه الحالة أصبح الكلاس A يرث من الكلاس c.
مثال 1
package a; class c { private int x1=6; protected int x2=7; public int x3=10; } public class A extends c { public static void main(String []args){ A ob1=new A(); System.out.println(ob1.x2); } }
في البداية قمنا بإنشاء كلاس أب وهو class c وهذا يعتبر الأب. و أنشأنا ثلاث متغيرات بدرجات وصول مختلفة وهي المتغير x1 كانت درجة الوصول له محمية private. ,والمتغير الثاني بدرجة وصول منخفضة protected قليلاً وهو x2., والثالث هو متغير عام x3. ثم ورثنا الإبن A من الأب c, و بداخل البرنامج الرئيسي قمنا بالطلب من البرنامج أن يطبع قيمة x2. عند الطباعة سوف تكون النتيجة:
7
نلاحظ عند التنفيذ لهذا البرنامج أننا قمنا بالوصول لقيمة المتغير x2 كما طلبنا منه و طبعها بواسطة الكائن ob. و نستطيع ايضاً الوصول لقيمة x3 بنفس الأمر لكن قيمة المتغير x1 لا نستطيع الوصول لها لأنه محمي.
مثال 2
نفترض أننا لدينا مصنع سيارات و هو يمتلك نوعين model A و model B.
package model_a; class Model_B { int speed; int size; String Model; } public class Model_A { int speed; int size; String Model; public static void main(String []args){ } }
كما نلاحظ أن هذا المثال به كلاسين و كل كلاس به ثلاث متغيرات يعني كان في البرنامج 6 متغيرات و سوف يحتجز مساحة من الذاكرة. هذه المتغيرات سوف تقوم بحجز مساحة كبيرة من الذاكرة. نستطيع إختصار هذا الكود بواسطة الوراثة لكتابة الخصائص المشتركة بين جميع الكلاسات و وضعها في. كلاس واحد ثم نجعل بقية الكلاسات يرثون من هذا الكلاس حتى نبتعد عن تعريف المتغيرات قدر الإمكان.
مثال 3
package model_a; class car { int speed; int size; String Model; } class Model_B extends car { } public class Model_A extends car{ public static void main(String []args){ Model_A A=new Model_A(); Model_B B=new Model_B(); A.speed=200; B.speed=250; System.out.println(A.speed); System.out.println(B.speed); } }
لقد أنشأنا كلاس أب إسمه car و كان به ثلاث متغيرات إثنان متغيرات عددية و الأخير متغير نصي و هي لمواصفات سيارات موجودة في هذا المعرض. يعني قمنا بتعريف الخصائص المشتركة في الكلاسين التي هي الدوال Function و المتغيرات المتشابهة المكررة في كل كلاس و وضعناهم بكلاس أب. ثم جعلنا بقية الكلاسات ترث من الكلاس الأب و هو الكلاس Model_B ورث من الكلاس الأب و ثم. الكلاس Model_A ايضاً أورثناه من الأب الذي هو car و هذا الكلاس أصبح يصل لكافة المتغيرات و الدوال ما عدا المتغيرات المحمية. و هنا تم إختصار الكود عوضاً عن ما كان به بالمثال السابق و إختصرنا الكود و حررنا مساحة كبيرة في الذاكرة.
ثم دخلنا للبرنامج الرئيسي, سنقوم بإنشاء كائن من كل كلاس حتى يستطيع الوصول للمتغيرات و الدوال الموجودة في الأب و إعادة إستخدامها وقت الحاجة. الكلاسات هي Model_A A و الكلاس Model_B B و الآن أصبح بالإمكان الوصول للمتغيرات و الدوال الموجودة في الأب ثم طبعناهم و كانت النتيجة.
200 250
الوراثة الهرمية Hierarchical في لغة جافا
تحدثنا في بداية الدرس بأن الكلاس الوارث يسمى الكلاس الإبن و الكلاس المورّث يسمى الكلاس الأب. نفس المفهوم هنا لكن سنتفرع قليلاً, سيكون لدينا وراثة هرمية بحيث يكون لدينا كلاس جد و كلاس إبن و كلاس حفيد. و لب الموضوع بهذه الفقرة هو هل يستطيع الكلاس الحفيد الوصول إلى متغيرات و دوال كلاس الجد.
مثال 4
package model_a; class A /الأب { # } class B extends A { /الإبن } B هذا الكلاس يعتبر ابن للكلاس A ويعتبر حفيد للكلاس public class C extends B{ public static void main(String []args){ System.out.println("Hello World"); } }
كما نلاحظ من خلال الكود السابق بأنه يحتوي على ثلاث كلاسات و هي الكلاس A و الكلاس B و الكلاس C. و كان الكلاس C هو الأب و الكلاس B هو الإبن للكلاس A و الكلاس C يعتبر إبن للكلاس B و يعتبر حفيد للكلاس A يعني جده هو A. و هنا سيكون سؤالنا هو هل يستطيع الكلاس C أن يصل لمتغيرات و دوال جده الكلاس A أم لا.
مثال 5
package C; class A { int x= 10; void print() { System.out.println(x); } } class B extends A { } public class C extends B { public static void main(String []args){ C ob=new C(); ob.x=20; ob.print(); } }
يحتوي البرنامج على ثلاث كلاسات و هي الكلاس A و الكلاس B و الكلاس C و كان الكلاس C هو الأب. و الكلاس B هو الإبن للكلاس A و الكلاس C يعتبر إبن للكلاس B و يعتبر حفيد للكلاس A يعني جده هو A. ثم دخلنا للبرنامج الرئيسي و إشتقينا كائن من الكلاس C و هو ob ثم طلبنا الوصول للمتغير x الذي في جده. و أعطيناه قيمة 20 ثم إستدعينا دالة print الموجودة في الكلاس A لطباعة قيمة x و عمل هذه الدالة هو طباعة قيمة x.
الكلمة super في جافا
كلمة super هي كلمة محجوزة في Java, في هذا الدرس سوف نتحدث عن الكلمة المحجوزة super و تعني هذه الكلمة أننا نستطيع من خلالها الوصول إلى دوال و متغيرات الكلاس الأب و كذلك دوال البناء.
مثال 6
package a1; class B { String name="My name is Badria"; } public class A1 extends B{ String name="My name is Muhammad"; A1() { System.out.println(name); System.out.println(super.name); } public static void main(String []args){ new A1 (); } }
في البداية قمنا بإنشاء كلاس أب وهو الكلاس B و به متغير نصي إسمه name. و من ثم اورثنا الكلاس A1 من الأب B و ايضاً به نفس المتغير الذي في الأب هنا تطبق مبدأ Overrid. ثم بنينا دالة بناء A1 و ثم طلبنا طباعة قيمة المتغير name الخاص بالكلاس الابن. ثم طلبنا الوصول لقيمة المتغير name الخاصة بالكلاس الاب بواسطة استخدام super, و بالبرنامج الرئيسي إستدعينا الكلاس الابن A1.
عند تشغيل الكود ستكون نتيجته:
My name is Muhammad My name is Badria
مثال 7
package a1; class B { String name="My name is Badria"; void show() { System.out.println(name); } } public class A1 extends B{ String name="My name is Muhammad"; A1() { System.out.println(name); super.show(); } public static void main(String []args){ new A1 (); } }
قمنا بإنشاء كلاس أب وهو الكلاس B و به متغير نصي إسمه name و به دالة void إسمها show و هذه الدالة تقوم بطباعة المتغير name و سوف تطبع تلقائياً طباعة name الخاصة بالكلاس الأب لأنه قمنا بالعملية بداخل الكلاس الأب. و من ثم اورثنا الكلاس A1 من الأب B و ايضاً به نفس المتغير الذي في الأب هنا تطبق مبدأ Overrid. ثم بنينا دالة بناء A1 و ثم طلبنا طباعة قيمة المتغير name الخاص بالكلاس الابن. ثم استدعينا الدالة show الخاصة بالأب عبر الكلمة super و هذه الدالة وضعنا بها طباعة القيمة الخاصة بالأب.
عند تشغيل هذا الكود سنحصل على النتيجة
My name is Muhammad My name is Badria
نستنتج بأن الكلاس الحفيد يستطيع الوصول إلى كافة متغيرات و دوال. الكلاس الجد ما عدا تلك الدوال و المتغيرات المحمية private لأنها هي خاصة بالكلاس.