🎭 تعدد الأشكال (Polymorphism) في جافا: سر المرونة والذكاء في البرمجة الكائنية
مرحباً بك في درس جديد من سلسلة مفاهيم البرمجة الكائنية (OOP) في جافا! 🎯 اليوم سنتعرف على واحد من أقوى وأهم هذه المبادئ: تعدد الأشكال (Polymorphism). كلمة "Polymorphism" تأتي من اليونانية وتعني "عدة أشكال". تخيل معي أنك تملك جهاز تحكم عن بُعد واحد يمكنه تشغيل التلفاز، والمكيف، ومشغل الصوت! هذا هو جوهر تعدد الأشكال في البرمجة.
🤔 ما هو تعدد الأشكال (Polymorphism)؟
تعدد الأشكال هو المبدأ الذي يسمح لكائن واحد بأن يتخذ عدة أشكال أو أن يتصرف بعدة طرق مختلفة، حسب السياق. ببساطة، هو القدرة على استخدام نفس الاسم (مثل اسم دالة) لأداء مهام مختلفة أو متنوعة.
في جافا، يتحقق تعدد الأشكال عندما نتعامل مع الكائنات من خلال الكلاس الأب (Parent Class) أو الواجهة (Interface)، حتى لو كانت هذه الكائنات منتمية إلى كلاسات أبناء (Child Classes) مختلفة. هذا يجعل الكود أكثر مرونة وقابلية للتوسع.
⚙️ كيف يتحقق تعدد الأشكال في جافا؟
يوجد نوعان رئيسيان لتعدد الأشكال في جافا:
1. تعدد الأشكال أثناء وقت التصريف (Compile-Time Polymorphism)
يُعرف أيضاً بـ Overloading. هنا، يقرر المترجم (Compiler) أي دالة سيتم استدعاؤها قبل تشغيل البرنامج (أثناء التصريف). يحدث هذا عندما يكون لدينا أكثر من دالة بنفس الاسم في نفس الكلاس، ولكن مع اختلاف في المعطيات (Parameters) (عددها أو نوعها).
مثال واقعي: فكر في آلة حاسبة. زر الجمع (+) يجمع رقمين، ويمكنه أيضاً جمع ثلاثة أرقام. نفس الاسم (الجمع)، سلوكيات مختلفة حسب المدخلات.
class Calculator {
// دالة لجمع عددين
int sum(int a, int b) {
return a + b;
}
// نفس اسم الدالة ولكن لجمع ثلاثة أعداد (Overloading)
int sum(int a, int b, int c) {
return a + b + c;
}
// نفس اسم الدالة ولكن لجمع أعداد عشرية
double sum(double a, double b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println(calc.sum(5, 10)); // يستدعي sum(int, int)
System.out.println(calc.sum(5, 10, 15)); // يستدعي sum(int, int, int)
System.out.println(calc.sum(5.5, 2.3)); // يستدعي sum(double, double)
}
}
2. تعدد الأشكال أثناء وقت التشغيل (Run-Time Polymorphism)
يُعرف أيضاً بـ Overriding. هنا، يقرر جهاز جافا الافتراضي (JVM) أي دالة سيتم استدعاؤها أثناء تشغيل البرنامج. هذا هو القلب الحقيقي لتعدد الأشكال ويحدث من خلال التوريث (Inheritance) و تجاوز الدوال (Method Overriding).
الفكرة: نعلن عن كائن من نوع الكلاس الأب، لكننا ننشئه فعلياً ككائن من نوع الكلاس الابن. وعند استدعاء دالة مُجاوزة (Overridden)، سينفذ JVM نسخة الدالة الموجودة في الكلاس الابن الفعلي للكائن.
مثال واقعي: تخيل أن لديك فئة عامة اسمها Animal (حيوان) بها دالة makeSound() (أصدر صوتاً). كل حيوان يصدر صوتاً مختلفاً.
// الكلاس الأب
class Animal {
public void makeSound() {
System.out.println("الحيوان يصدر صوتاً");
}
}
// الكلاس الابن الأول
class Dog extends Animal {
// تجاوز (Override) لدالة makeSound
@Override
public void makeSound() {
System.out.println("الكلب يقول: هوه هوه!");
}
}
// الكلاس الابن الثاني
class Cat extends Animal {
// تجاوز (Override) لدالة makeSound
@Override
public void makeSound() {
System.out.println("القطة تقول: مياوو!");
}
}
public class Main {
public static void main(String[] args) {
// هنا يظهر تعدد الأشكال بوضوح
Animal myAnimal; // المتغير من نوع الكلاس الأب
myAnimal = new Dog(); // الكائن الفعلي من نوع Dog
myAnimal.makeSound(); // المخرجات: الكلب يقول: هوه هوه!
myAnimal = new Cat(); // نفس المتغير، كائن فعلي جديد من نوع Cat
myAnimal.makeSound(); // المخرجات: القطة تقول: مياوو!
// يمكننا حتى استخدام مصفوفة من النوع الأب
Animal[] animals = {new Dog(), new Cat(), new Dog()};
for (Animal a : animals) {
a.makeSound(); // كل كائن سينفذ الدالة الخاصة به!
}
}
}
💡 لماذا تعدد الأشكال مهم جداً؟
- إمكانية الصيانة: يمكنك إضافة كلاسات أبناء جديدة دون الحاجة لتغيير الكود الذي يعالج الكائنات من خلال الكلاس الأب.
- المرونة: يمكن كتابة كود عام يعمل مع مجموعة واسعة من الكائنات المختلفة.
- القابلية للتوسع: يسهل إضافة سلوكيات جديدة للنظام.
- تبسيط الكود: بدلاً من كتابة
ifأوswitchطويلة للتحقق من نوع كل كائن، نعتمد على تعدد الأشكال لاختيار السلوك المناسب تلقائياً.
🧠 ملخص الدرس
- تعدد الأشكال (Polymorphism) يعني "عدة أشكال" ويسمح للكائن بالتصرف بأكثر من طريقة.
- النوع الأول: Overloading (تعدد الأشكال أثناء التصريف): وجود دوال متعددة بنفس الاسم ولكن بمعطيات مختلفة في نفس الكلاس.
- النوع الثاني: Overriding (تعدد الأشكال أثناء التشغيل): هو الأقوى، ويحدث عبر التوريث، حيث يُعرِّف الكلاس الابن سلوكه الخاص لدالة موجودة في الأب.
- الفائدة الكبرى هي كتابة كود عام ومرن، مثل
Animal myAnimal = new Dog();حيث يقرر JVM في وقت التشغيل أي دالةmakeSound()سيُنفذ.
🎓 اختبر نفسك
التعليقات
شاركنا رأيك أو أسئلتك حول هذا المقال