الـ Sealed Classes (Java 17+)
جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 52 من 87.
في لغة Java التقليدية، عندما تقوم بإنشاء فئة (class)، يمكن لأي فئة أخرى توسيعها (ما لم تقم بتمييزها كـ final). ولكن ماذا لو كنت تريد السماح بالوراثة، ولكن ليس من أي شخص؟ قدم إصدار Java 17 الفئات المختومة (sealed classes) لمنحك تحكماً دقيقاً في الفئات التي يمكنها توسيع فئتك.
يُصرح الفصل المختوم (sealed class) صراحةً عن فئاته الفرعية المسموح بها باستخدام الكلمات الرئيسية sealed و permits:
sealed class Shape permits Circle, Rectangle, Triangle {
abstract double area();
}
final class Circle extends Shape {
double radius;
double area() { return Math.PI * radius * radius; }
}
final class Rectangle extends Shape {
double width, height;
double area() { return width * height; }
}
final class Triangle extends Shape {
double base, height;
double area() { return 0.5 * base * height; }
}فقط Circle و Rectangle و Triangle يمكنها توسيع Shape. أي فئة أخرى تحاول توسيعه ستتسبب في خطأ في التجميع (compilation error).
يجب أن تستخدم كل فئة فرعية مسموح بها أحد المعدلات الثلاثة: final (لا يوجد تمديد إضافي)، أو sealed (يواصل سلسلة القيود)، أو non-sealed (يفتح المجال للتمديد غير المقيد):
sealed class Vehicle permits Car, Truck { }
final class Car extends Vehicle { } // لا يمكن تمديدها
non-sealed class Truck extends Vehicle { } // يمكن لأي شخص تمديد Truckتعتبر الأصناف المختومة (Sealed classes) قوية بشكل خاص مع مطابقة الأنماط (pattern matching) في تعبيرات switch، حيث يعرف المترجم (compiler) جميع الأنواع الفرعية الممكنة ويمكنه التحقق من الشمولية. إنها مثالية عند نمذجة مجموعة ثابتة من الأنواع في مجال معين، مثل طرق الدفع، أو أنواع الاستجابة، أو الأشكال الهندسية.
التحدي
سهللنقم ببناء نظام إشعارات يوضح الفئات المختومة (sealed classes) من خلال تقييد أنواع الإشعارات التي يمكن أن توجد في تطبيقك. ستقوم بإنشاء تسلسل هرمي حيث يُسمح فقط بأنواع إشعارات محددة، وكل نوع يتعامل مع عملية التسليم بشكل مختلف.
ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:
Notification.java: قم بإنشاء فئة مجردة مختومة (sealed abstract class) تعمل كقاعدة لجميع الإشعارات. يجب أن تسمح بوجود ثلاث فئات فرعية بالضبط:EmailNotificationوSMSNotificationوPushNotification. تحتاج فئتك المختومة إلى حقل محميmessage(String)، ومنشئ (constructor) لتهيئته، ودالة getter للرسالة، ودالة مجردةdeliver()تعيد String يصف كيفية إرسال الإشعار.EmailNotification.java: قم بإنشاء فئةfinalترث من Notification. يحتوي EmailNotification على حقل خاص إضافيrecipient(String) لعنوان البريد الإلكتروني. يجب أن يأخذ المنشئ الخاص بها كلاً من الرسالة والمستلم، مع استدعاء منشئ الفئة الأب باستخدامsuper(). يجب أن تعيد الدالةdeliver():"Sending email to " + recipient + ": " + messageSMSNotification.java: قم بإنشاء فئةnon-sealedترث من Notification—وهذا يسمح لفئات أخرى بوراثتها إذا لزم الأمر في المستقبل. يحتوي SMSNotification على حقل خاص إضافيphoneNumber(String). يأخذ المنشئ الخاص بها الرسالة ورقم الهاتف. يجب أن تعيد الدالةdeliver():"Sending SMS to " + phoneNumber + ": " + messagePushNotification.java: قم بإنشاء فئةfinalترث من Notification. يحتوي PushNotification على حقل خاص إضافيdeviceId(String). يأخذ المنشئ الخاص بها الرسالة ومعرف الجهاز. يجب أن تعيد الدالةdeliver():"Sending push to device " + deviceId + ": " + messageMain.java: اجمع نظام الإشعارات الخاص بك معاً! ستتلقى أربعة مدخلات: رسالة (String)، وعنوان بريد إلكتروني (String)، ورقم هاتف (String)، ومعرف جهاز (String).قم بإنشاء واحد من كل نوع من أنواع الإشعارات باستخدام نفس الرسالة ولكن مع معلومات الاتصال الخاصة بكل منها. قم بتخزين الثلاثة جميعاً في مصفوفة من نوع
Notification[]لإظهار تعدد الأشكال (polymorphism) مع الفئات المختومة. ثم قم بالمرور عبر المصفوفة واطبع نتيجة استدعاءdeliver()لكل إشعار.
ستتلقى أربعة مدخلات بالترتيب: الرسالة، عنوان البريد الإلكتروني، رقم الهاتف، ومعرف الجهاز.
لاحظ كيف تقيد الفئة المختومة التسلسل الهرمي—فقط الفئات الثلاث المسموح بها يمكنها وراثة Notification. يجب على كل فئة فرعية أن تعلن عن نفسها كـ final أو sealed أو non-sealed. يمنحك هذا تحكماً كاملاً في تسلسل الأنواع الهرمي مع الاستمرار في تمكين تعدد الأشكال!
ورقة مرجعية
يُقيد الفصل المختوم (sealed class) الفئات التي يمكنها توسيعه باستخدام الكلمات المفتاحية sealed و permits:
sealed class Shape permits Circle, Rectangle, Triangle {
abstract double area();
}فقط الفئات المدرجة في permits يمكنها توسيع الفصل المختوم. أي فئة أخرى تحاول توسيعه ستتسبب في خطأ برمجِي أثناء التجميع (compilation error).
يجب أن تستخدم كل فئة فرعية مسموح بها أحد المعدلات الثلاثة التالية:
final- يمنع التوسيع الإضافيsealed- يواصل سلسلة القيود مع فئاته الفرعية المسموح بهاnon-sealed- يفتح التسلسل الهرمي للتوسيع غير المقيد
sealed class Vehicle permits Car, Truck { }
final class Car extends Vehicle { } // لا يمكن توسيعه
non-sealed class Truck extends Vehicle { } // يمكن لأي شخص توسيع Truckتُعد الفصول المختومة (Sealed classes) مفيدة لنمذجة مجموعة ثابتة من الأنواع في مجال معين، مثل طرق الدفع، أو أنواع الاستجابة، أو الأشكال الهندسية. وهي تعمل بشكل جيد مع مطابقة الأنماط (pattern matching) في تعبيرات switch، حيث يعرف المترجم (compiler) جميع الأنواع الفرعية الممكنة.
جرّب بنفسك
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// قراءة المدخلات الأربعة
String message = scanner.nextLine();
String email = scanner.nextLine();
String phoneNumber = scanner.nextLine();
String deviceId = scanner.nextLine();
// TODO: إنشاء واحد من كل نوع من أنواع الإشعارات باستخدام نفس الرسالة
// - EmailNotification مع message و email
// - SMSNotification مع message و phoneNumber
// - PushNotification مع message و deviceId
// TODO: تخزين جميع الإشعارات الثلاثة في مصفوفة Notification[]
// هذا يوضح تعدد الأشكال (polymorphism) مع الفئات المختومة (sealed classes)
// TODO: التكرار عبر المصفوفة وطباعة نتيجة deliver()
// لكل إشعار
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس 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أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكرر