الوراثة وتعدّد الأشكال

الوراثة والكلمة المفتاحية extends

15 دقيقة الدرس 1 من 14

الوراثة والكلمة المفتاحية extends

من أقوى الأفكار في البرمجة كائنية التوجه هي الوراثة — أي قدرة فئة ما على استلام حقول وتوابع فئة أخرى تلقائيًا. بدلًا من كتابة نفس الكود في خمسة أماكن، تكتبه مرّة واحدة في الفئة الأم (Superclass) وتتيح لكل فئة مشتقة (Subclass) ذات صلة إعادة استخدامه مجانًا.

علاقة "هو نوع من" (Is-A)

قبل أن تبدأ الكتابة، اطرح سؤالًا واحدًا: هل تمثّل فئتي الجديدة نسخة أكثر تحديدًا من فئة موجودة؟ إذا كان Dog (كلب) هو نوع من Animal (حيوان)، فهذه علاقة "هو نوع من"، والوراثة هي الأداة المناسبة. أما إذا كانت Car (سيارة) تحتوي على Engine (محرك)، فهذه علاقة "يمتلك" — استخدم التركيب بدلًا من الوراثة (سنتناول ذلك في درس لاحق).

  • Dog هو نوع من Animal — مناسب للوراثة.
  • SavingsAccount هو نوع من BankAccount — مناسب للوراثة.
  • Car تمتلك Engine — استخدم التركيب لا الوراثة.
اختبار "هو نوع من" هو أهم اختبار تصميمي. تطبيق الوراثة حيث يكون التركيب أنسب ينتج كودًا هشًا ومربكًا. اطرح السؤال في كل مرة قبل استخدام extends.

الفئة الأم والفئة المشتقة

في Java تُسمّى الفئة التي يُرِث منها غيرها الفئة الأم (Superclass)، وتُعرف أيضًا بالفئة الأساسية أو الفئة الجذر. أما الفئة التي ترِث فتُسمّى الفئة المشتقة (Subclass)، وتُعرف بالفئة الفرعية أو الفئة المُشتقّة. تُنشئ هذه العلاقة باستخدام الكلمة المفتاحية extends.

// الفئة الأم public class Animal { String name; int age; public void eat() { System.out.println(name + " is eating."); } public void sleep() { System.out.println(name + " is sleeping."); } } // الفئة المشتقة public class Dog extends Animal { String breed; public void bark() { System.out.println(name + " says: Woof!"); } }

تمتد Dog من Animal، لذا يمتلك كل كائن Dog تلقائيًا name وage وeat() وsleep() — دون سطر إضافي واحد داخل فئة Dog. تعرّف فئة Dog فقط ما يجعل الكلب مختلفًا عن الحيوان العام: حقل breed والتابع bark().

ما الذي يُوَرَّث؟

ترث الفئة المشتقة كل ما تعلنه الفئة الأم بصلاحية public أو protected. تحديدًا:

  • حقول الكائن (Instance Fields) — مثل name وage في المثال أعلاه.
  • توابع الكائن (Instance Methods) — مثل eat() وsleep().
  • الأعضاء الساكنة (Static Members) — تُوَرَّث أيضًا، وإن كانت التوابع الساكنة لا تخضع للتوزيع متعدد الأشكال.

ما لا يُوَرَّث:

  • الأعضاء الخاصة (Private) — الحقول والتوابع المُعلَنة بـ private موجودة في كائن الفئة الأم لكن لا يمكن الوصول إليها مباشرةً من الفئة المشتقة.
  • المُنشئات (Constructors) — لا تُوَرَّث أبدًا؛ يجب أن تعرّف الفئة المشتقة مُنشئها الخاص (أو يضيف المُترجم مُنشئًا افتراضيًا). يمكنك استدعاء مُنشئ الفئة الأم باستخدام super()، وهو ما سنتناوله في الدرس التالي.
الحقول الخاصة غير قابلة للوصول، لكنها موجودة. عند إنشاء كائن Dog، يُهيَّأ الجزء المتعلق بـ Animal بالكامل. لكنك لا تستطيع قراءة أو كتابة الحقول private في الفئة الأم مباشرةً؛ استخدم protected أو توابع getter العامة بدلًا من ذلك.

مثال قابل للتشغيل

لنجمع كل ما سبق ونشغّله:

public class Main { public static void main(String[] args) { Dog myDog = new Dog(); myDog.name = "Buddy"; // موروث من Animal myDog.age = 3; // موروث من Animal myDog.breed = "Labrador"; // مُعرَّف في Dog myDog.eat(); // تابع موروث myDog.sleep(); // تابع موروث myDog.bark(); // تابع خاص بـ Dog } }

المُخرجات:

Buddy is eating. Buddy is sleeping. Buddy says: Woof!

لم تعرّف Dog eat() أو sleep()، ومع ذلك استدعيناهما على كائن من نوع Dog دون أي مشكلة. هذه هي الوراثة في العمل.

التمديد المتعدد المستويات (Multi-Level Inheritance)

تتيح Java أن تكون الفئة المشتقة بدورها فئةً أمًا لفئة أخرى. يمكنك إنشاء سلسلة مثل AnimalDogGuideDog. كل مستوى يرث كل شيء من المستويات الأعلى منه.

public class GuideDog extends Dog { String owner; public void guide() { System.out.println(name + " is guiding " + owner); } }

يمتلك كائن GuideDog الآن name وage وbreed وeat() وsleep() وbark() وguide() — مُتراكِمة عبر ثلاثة مستويات.

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

لماذا تهمّ الوراثة؟

تمنحك الوراثة ثلاث فوائد ملموسة:

  1. إعادة استخدام الكود — اكتب السلوك المشترك مرّة واحدة في الفئة الأم بدلًا من تكراره عبر فئات متعددة.
  2. قابلية التوسع — أضف نوعًا جديدًا من Animal بإنشاء فئة مشتقة جديدة؛ الكود المشترك لا يتغير.
  3. تعدد الأشكال (Polymorphism) — يمكن لمتغير من نوع Animal أن يحمل Dog أو Cat أو أي فئة مشتقة أخرى. هذا موضوع الدرس الخامس ويفتح أمامك بعضًا من أقوى أنماط Java.

الخلاصة

استخدم الكلمة المفتاحية extends عندما تكون للفئة الجديدة علاقة "هو نوع من" حقيقية مع فئة موجودة. ترث الفئة المشتقة جميع أعضاء الفئة الأم ذات الصلاحية public أو protected، وتضيف تخصصاتها الخاصة فوقها. لا تُوَرَّث المُنشئات ولا الأعضاء private. في الدرس التالي سنتناول super وكيفية استدعاء مُنشئ الفئة الأم بشكل صحيح.