إعادة تعريف الدوال (@Override)
جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 22 من 87.
يسمح تجاوز الطريقة (Method overriding) للفئة الفرعية (subclass) بتقديم تنفيذها الخاص لدالة (method) معرفة مسبقاً في فئتها الأب (parent class). هذه هي الطريقة التي تخصص بها الفئات الأبناء السلوك الموروث لتناسب احتياجاتها الخاصة.
عندما تقوم بإعادة تعريف (override) دالة (method)، فإن نسخة الفئة الفرعية (subclass) تحل محل نسخة الفئة الأب (parent) تماماً بالنسبة لكائنات تلك الفئة الفرعية. يجب أن تمتلك الدالة نفس الاسم، ونوع الإرجاع، والمعاملات (parameters) كما في دالة الفئة الأب:
public class Animal {
public void makeSound() {
System.out.println("Some generic sound");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}تخبر الملاحظة التوضيحية @Override المترجم (compiler) أنك تنوي تجاوز (override) طريقة في الفئة الأب. ورغم أنها اختيارية، إلا أنه يوصى بها بشدة لأن المترجم سيكتشف الأخطاء إذا أخطأت في كتابة اسم الطريقة أو استخدمت معاملات خاطئة:
@Override
public void makeSound() { } // صحيح - المترجم يتحقق من هذا
public void makesound() { } // خطأ مطبعي! ينشئ طريقة جديدة بدلاً من التجاوزعندما تستدعي طريقة تم تجاوزها (overridden method)، يقوم Java بتنفيذ الإصدار الذي ينتمي إلى نوع الكائن الفعلي:
Dog dog = new Dog();
dog.makeSound(); // النتيجة: Woof!
Cat cat = new Cat();
cat.makeSound(); // النتيجة: Meow!تذكر من الدرس السابق أنه يمكنك استخدام super.methodName() داخل طريقة تم تجاوزها (overridden method) إذا كنت لا تزال ترغب في تضمين سلوك الفئة الأب مع الكود الجديد الخاص بك.
التحدي
سهللنقم ببناء نظام إشعارات يوضح كيف يمكن للفئات الفرعية (subclasses) تجاوز (override) أساليب الفئة الأب لتقديم سلوكها المتخصص. سترى كيف يساعد التعليق التوضيحي @Override في اكتشاف الأخطاء وكيف يمكن لكل فئة فرعية تخصيص الأساليب الموروثة.
ستقوم بإنشاء ثلاثة ملفات لتنظيم الكود الخاص بك:
Notification.java: أنشئ الفئة الأب التي تمثل إشعاراً عاماً. يجب أن تحتوي على:- حقل خاص (private field) لـ
message(String) - منشئ (constructor) يقبل الرسالة
- أسلوب
getMessage()يعيد الرسالة - أسلوب
send()يطبع:Sending notification: [message]
- حقل خاص (private field) لـ
EmailNotification.java: أنشئ فئة فرعية ترث من Notification وتخصص كيفية إرسال الإشعارات عبر البريد الإلكتروني:- حقل خاص لعنوان البريد الإلكتروني
recipient(String) - منشئ يأخذ كلاً من الرسالة والمستلم، باستخدام
super(message)للجزء الخاص بالفئة الأب - تجاوز أسلوب
send()باستخدام التعليق التوضيحي@Overrideلطباعة:Emailing [recipient]: [message]
- حقل خاص لعنوان البريد الإلكتروني
Main.java: وضح كيف ينتج نفس اسم الأسلوب سلوكاً مختلفاً اعتماداً على نوع الكائن. ستتلقى مدخلين: رسالة وعنوان بريد إلكتروني. أنشئ كلاً من Notification عادي و EmailNotification بنفس الرسالة، ثم استدعِsend()على كل منهما لرؤية المخرجات المختلفة.
ستتلقى مدخلين: الرسالة (String) وعنوان البريد الإلكتروني للمستلم (String).
يجب أن يظهر مخرجك سطرين - الأول من أسلوب send() الخاص بالفئة الأب، ثم من النسخة التي تم تجاوزها في EmailNotification. يوضح هذا كيف يسمح تجاوز الأساليب (method overriding) للفئات الفرعية باستبدال السلوك الموروث بتنفيذها الخاص.
ورقة مرجعية
يسمح تجاوز الدوال (Method overriding) للفئة الفرعية (subclass) بتقديم تنفيذ خاص بها لدالة معرفة مسبقاً في الفئة الأب (parent class). يجب أن تمتلك الدالة التي تم تجاوزها نفس الاسم، ونوع الإرجاع، والمعاملات الخاصة بالدالة في الفئة الأب.
public class Animal {
public void makeSound() {
System.out.println("Some generic sound");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}يُعد التعليق التوضيحي @Override اختيارياً ولكنه موصى به بشدة. فهو يخبر المترجم (compiler) أنك تنوي تجاوز دالة في الفئة الأب، مما يساعد في اكتشاف الأخطاء مثل كتابة أسماء الدوال بشكل خاطئ أو استخدام معاملات غير صحيحة:
@Override
public void makeSound() { } // صحيح - يتحقق المترجم من هذا
public void makesound() { } // خطأ مطبعي! ينشئ دالة جديدة بدلاً من التجاوزعند استدعاء دالة تم تجاوزها، تقوم Java بتنفيذ الإصدار الذي ينتمي إلى نوع الكائن الفعلي:
Dog dog = new Dog();
dog.makeSound(); // Output: Woof!
Cat cat = new Cat();
cat.makeSound(); // Output: Meow!يمكنك استخدام super.methodName() داخل الدالة التي تم تجاوزها لتضمين سلوك الفئة الأب جنباً إلى جنب مع الكود الجديد الخاص بك.
جرّب بنفسك
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// قراءة المدخلات
String message = scanner.nextLine();
String recipient = scanner.nextLine();
// TODO: إنشاء كائن Notification مع الرسالة
// TODO: إنشاء كائن EmailNotification مع الرسالة والمستلم
// TODO: استدعاء send() على كائن Notification
// TODO: استدعاء send() على كائن EmailNotification
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةمقدمة في الـ OOPالفئات (Classes) مقابل الكائنات (Objects)الكلمة المفتاحية thisالدوال (Methods)الحقول (Attributes)دالة البناء (Constructor)تعدد أشكال دالة البناء (Constructor Overloading)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسية (extends)الكلمة المفتاحية superإعادة تعريف الدوال (@Override)تسلسل المنشئات (Constructor Chaining)كلاس Objectالوراثة الأحادية ومتعددة المستوياتلماذا لا توجد وراثة متعددة للكلاساتمراجعة - هيكلية الموظفين7الدوال الخاصة وفئة Object
دالة toString()equals() و hashCode()دالة clone()compareTo() و Comparableواجهة Comparatorمراجعة - الترتيب المخصص2محددات الوصول والتغليف
نظرة عامة على مستويات الوصولدوال Getter و Setterإخفاء البياناتالكلمة المفتاحية finalمراجعة - مدير الحساب البنكي5تعدد الأشكال (Polymorphism)
أساسيات الـ Method Overloadingالـ Method Overriding (في وقت التشغيل)الـ Upcasting والـ Downcastingمعامل الـ instanceofالفئات والدوال المجردة (Abstract)مراجعة - حاسبة الأشكال8مفاهيم OOP المتقدمة
الـ Composition مقابل الـ Inheritanceالـ Aggregation مقابل الـ Compositionالفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)الـ Enums ودوالهاالـ Records (Java 16+)الـ Sealed Classes (Java 17+)11أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Builderنمط Observerنمط Strategy3خصائص الفئة والعضو الساكن
المتغيرات الخاصة بالكائن مقابل المتغيرات الساكنةالدوال الساكنةالكتل البرمجية الساكنةالثوابت (static final)ملخص - العداد والأدوات المساعدة6الواجهات (Interfaces) والأصناف المجردة (Abstract Classes)
مقدمة في الواجهات (Interfaces)تنفيذ الواجهات (Interfaces)تنفيذ واجهات متعددةالـ Default و Static في الواجهاتالأصناف المجردة مقابل الواجهاتالواجهات الوظيفية (Functional Interfaces)مراجعة - نظام الدفع9الأنواع العامة (Generics)
مقدمة في الأنواع العامةالأصناف العامة (Generic Classes)الدوال العامة (Generic Methods)معاملات الأنواع المقيدةالرموز البديلة (?, extends, super)مراجعة - الحاويات العامة12أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكرر