مفهوم النوع enum
في جافا
enum
هي كلمة محجوزة في جافا, تستخدم لتعريف عدة ثوابت ضمن مجموعة واحدة بشكل منطقي.
و في النهاية تعتبر هذه المجموعة كنوع جديد من البيانات و فيها عدة قيم محتملة.
النوع enum
مفيد جداً في حال أردت بناء كود مخصص فقط للتعامل مع نوع محدد من القيم و في حال حاول أي مبرمج آخر عدم الإلتزام بنوع البيانات الذي فرضته أنت و قام بتمرير قيم من نوع آخر فإنه يتم تنبيهه مباشرةً قبل تشغيل الكود و في حال أصر على ذلك و قام بتشغيل الكود فإن مترجم لغة جافا سيمنعه وقت التشغيل و يظهر له خطأ.
بشكل عام, في حال أردت تعريف مجموعة قيم مترابطة يستحيل أن تتبدل فالخيار الأمثل هو تعريف هذه القيم في الأساس بداخل enum
.
أمثلة حول بعض المعلومات الثابتة في الحياة و التي أيضاً تعتبر ثابتة في المنطق هي:
فصول السنة ( الخريف, الشتاء, الربيع, الصيف )
الإتجاهات ( الشمال - الجنوب - الشرق - الغرب )
أيام الأسبوع ( الإثنين - الثلاثاء - الأربعاء إلخ.. )
أشهر السنة ( كانون الثاني - شباط - آذار إلخ.. )
الجنس ( ذكر - أنثى )
عند التعامل مع هذا النوع الثابت من البيانات و الذي لا نريد لأحد أن يعبث به فإننا نستخدم النوع enum
و هذا ما سنراه لاحقاً في الأمثلة.
مصطلحات تقنية النوع enum
الـ enum
يقال له تعداد في اللغة العربية حيث نضع فيه مجموعة عناصر.
التعامل مع النوع enum
في جافا
التعامل مع الـ enum
يشبه كثيراً التعامل مع الكلاس المعرف كـ final static
حيث لا يمكن إنشاء كائنات منه و يمكن الوصول بشكل مباشر للثوابت الموضوعة فيه.
نقاط مهمة حول النوع enum
يزيد أمان أنواع البيانات التي سيتم التعامل معها.
يمكن إستخدامه في الجملة
switch
.يمكن تعريفه ضمن ملف خاص مثل الكلاس و يمكن تعريفه ضمن كلاس.
كل ثابت فيه, عبارة عن كائن من نفس نوعه و يعتبر نوعه
public final static
.يمكنه أن يحتوي على متغيرات, دوال, كونستركتورات.
لا يمكن إنشاء كائنات منه و لا داعي لذلك أصلاً لأنه يمكن الوصول لثوابته بشكل مباشر.
عند تعريف كونستركتور فيه فإنه يجب تعريفه كـ
private
لأنه لا يمكن إنشاء كائنات منه و هذا أمر منطقي.يمكنه أن يفعل
implements
لإنترفيس أو أكثر.لا يمكنه أن يرث من أي كلاس لأنه في الواقع يرث من كلاس إسمه
Enum
.يمكنك إستدعاء الدالة
values()
من أيenum
للحصول على مصفوفة من نفس نوعه و فيها نسخة من كل قيمه.
معلومة تقنية النوع enum
كل ثابت تضعه في الـ enum
يتم إعطاؤه رقم Index ابتداءاً من الرقم 0 يشير لموقعه فيه مثل المصفوفة.
فمثلاً العنصر الأول يعطى Index رقم 0 و العنصر الثاني يعطى Index رقم 1 و هكذا.
يمكنك معرفة رقم الـ Index المعطى لأي ثابت في الـ enum
بواسطة الدالة ordinal()
و لكنك على الأغلب لن تحتاج إلى ذلك.
أمثلة شاملة حول النوع enum
في جافا
كيف تقوم بتعريف enum
بداخل كلاس
ستتعلم من المثال التالي كيف تقوم بتعريف enum
بداخل كلاس بالإضافة إلى معرفة كيف يمكن الوصول للثوابت الموجودة فيه و معرفة Index الثابت.
في المثال التالي قمنا بتعريف كلاس عادي و بداخله قمنا بتعريف enum
يمثل أيام الأسبوع إسمه Days
.
في الدالة main()
قمنا بعرض قيمة أول ثابت موجود في الـ enum
بالإضافة إلى رقم الـ Index الخاص فيه.
مثال
public class Main { // وضعنا فيه 7 ثوابت Days إسمه enum هنا قمنا بتعريف enum Days { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } public static void main(String[] args) { // Days الموجود في التعداد MONDAY هنا قمنا بطباعة إسم الثابت System.out.println(Days.MONDAY); // Days بالنسبة للتعداد MONDAY الثابت index هنا قمنا بطباعة رقم System.out.println(Days.MONDAY.ordinal()); } }
•سنحصل على النتيجة التالية عند التشغيل.
0
كيف تقوم بعرض جميع الثوابت الموجودة في الـ enum
ستتعلم من المثال التالي كيف تقوم بعرض جميع الثوابت الموجودة في الـ enum
بالإعتماد على الدالة values()
و الحلقة for each.
في المثال التالي قمنا بتعريف enum
يمثل أيام الأسبوع إسمه Days
.
في الدالة main()
قمنا باستخدام الدالة values()
و التي ترجع مصفوفة فيها جميع عناصر التعداد Days
. عناصر هذه المصفوفة قمنا بعرضها مباشرة باستخدام حلقة For Each.
مثال
public class Main { // وضعنا فيه 7 ثوابت Days إسمه enum هنا قمنا بتعريف enum Days { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } public static void main(String[] args) { // و ستعرض كل عنصر فيها values() هذه الحلقة ستقوم بالمرور على جميع العناصر التي سترجعها الدالة for(Days d: Days.values()) { System.out.println(d); } } }
•سنحصل على النتيجة التالية عند التشغيل.
TUESDAY
WEDNESDAY
THURSDAY
FRIDAY
SATURDAY
SUNDAY
طريقة تخزين ثابت موجود في enum
بداخل كائن من نفس نوعه
ستتعلم من المثال التالي كيف تقوم بتخزين ثابت موجود في enum
بداخل كائن من نفس نوعه.
في المثال التالي قمنا بتعريف enum
يمثل أيام الأسبوع إسمه Days
.
في الدالة main()
قمنا بتعريف كائن نوعه Days
و إسمه day
وضعنا فيه نسخة من رابع ثابت موجود في الـ Days
.
مثال
public class Main { // وضعنا فيه 7 ثوابت Days إسمه enum هنا قمنا بتعريف enum Days { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } public static void main(String[] args) { // THURSDAY قيمته تساوي قيمة الثابت Days و نوعه day هنا قمنا بتعريف كائن إسمه Days day = Days.THURSDAY; System.out.println(day); } }
•سنحصل على النتيجة التالية عند التشغيل.
كيف تقوم بتحديد قيمة كل ثابت في الـ enum
ستتعلم من المثال التالي كيف تقوم بتحديد قيمة كل ثابت في الـ enum
.
في المثال التالي قمنا بتعريف enum
إسمه Size
, وضعنا فيه 4 ثوابت مع وضع قيمة عددية لكل ثابت.
في الدالة main()
قمنا بعرض كل ثابت و بجانبه القيمة التي يمثلها.
مثال
public class Main { // وضعنا فيه 4 ثوابت مع تحديد قيمهم Size إسمه enum هنا قمنا بتعريف enum Size { // هنا قمنا بتعريف أسماء الثوابت و تحديد قيمهم SMALL(100), MEDIUM(150), LARGE(200), XLARGE(250); // هنا قمنا بتعريف المتغير الذي سيتم إستخدامه بشكل تلقائي لتخزين قيمة كل ثابت تم تعريفه private int value; // هنا قمنا بتعريف الكونستركتور الذي سيقوم بربط إسم كل ثابت بالقيمة التي تم وضعها بجانبه private Size(int value){ this.value = value; } } public static void main(String[] args) { // و ستعرض إسم و قيمة كل عنصر فيها values() هذه الحلقة ستقوم بالمرور على جميع العناصر التي سترجعها الدالة for(Size s: Size.values()) { System.out.println(s + " " + s.value); } } }
•سنحصل على النتيجة التالية عند التشغيل.
MEDIUM 150
LARGE 200
XLARGE 250
كيف تضع أكثر من قيمة لكل ثابت في الـ enum
ستتعلم من المثال التالي كيف تضع أكثر من قيمة لكل ثابت في الـ enum
.
في المثال التالي قمنا بتعريف enum
إسمه Size
, وضعنا فيه 4 ثوابت مع وضع قيمة عددية و قيمة نصية لكل ثابت إسم.
في الدالة main()
قمنا بعرض القيمة النصية و القيمة العددية لكل ثابت.
مثال
public class Main { // وضعنا فيه 4 ثوابت مع تحديد قيمهم Size إسمه enum هنا قمنا بتعريف enum Size { // هنا قمنا بتعريف الثوابت مع إعطاء كل واحد منهم رقم يمثل قيمة الثابت و نص يمثل إسم الثابت SMALL(100, "small"), MEDIUM(150, "meduim"), LARGE(200, "large"), XLARGE(250, "x-large"); // هنا قمنا بتعريف المتغيرين اللذي سيتم إستخدامهما بشكل تلقائي لتخزين إسم و قيمة كل ثابت تم تعريفه private int value; private String name; // هنا قمنا بتعريف الكونستركتور الذي سيقوم بربط كل ثابت بالإسم و القيمة التي تم وضعها بجانبه private Size(int value, String name){ this.value = value; this.name = name; } } public static void main(String[] args) { // لكل عنصر فيها value و الـ name و ستعرض قيمة الـ values() هذه الحلقة ستقوم بالمرور على جميع العناصر التي سترجعها الدالة for(Size s: Size.values()) { System.out.println(s.name + " " + s.value); } } }
•سنحصل على النتيجة التالية عند التشغيل.
meduim 150
large 200
x-large 250
كيف تعرف قيمة الثابت الذي إختاره المستخدم
ستتعلم من المثال التالي كيف تعرف قيمة الثابت الذي إختاره المستخدم من الـ enum
بواسطة الجملة switch
.
في المثال التالي قمنا بتعريف enum
يمثل أيام الأسبوع إسمه Days
.
في الدالة main()
قمنا بتعريف كائن نوعه Days
و إسمه userSelectedDay
وضعنا فيه نسخة من رابع ثابت موجود في الـ Days
و اعتبرنا أن المستخدم هو من قام باختيار قيمة هذا الثابت. بعدها قمنا بمقارنة الثابت userSelectedDay
مع جميع الثوابت الموجودة في التعداد Days
بواسطة الجملة switch
.
مثال
public class Main { // وضعنا فيه 7 ثوابت Days إسمه enum هنا قمنا بتعريف enum Days { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } public static void main(String[] args) { // THURSDAY قيمته تساوي قيمة الثابت Days و نوعه userSelectedDay هنا قمنا بتعريف كائن إسمه Days userSelectedDay = Days.THURSDAY; // userSelectedDay هنا قمنا باختبار نوع الثابت switch (userSelectedDay) { // سيتم تنفيذ أمر الطباعة التالي THURSDAY أو WEDNESDAY أو TUESDAY أو MONDAY إذا كان يمثل نسخة من الثابت case MONDAY: case TUESDAY: case WEDNESDAY: case THURSDAY: System.out.println("We are available from 8:00 AM to 4:00 PM."); break; // سيتم تنفيذ أمر الطباعة التالي FRIDAY إذا كان يمثل نسخة من الثابت case FRIDAY: System.out.println("We are available from 8:00 AM to 12:00 PM."); break; // إذا كان لا يمثل نسخة من الثوابت المذكورة سابقاً سيتم تنفيذ أمر الطباعة التالي default: System.out.println("We are not available on vacations."); } } }
•سنحصل على النتيجة التالية عند التشغيل.
كيف تقوم بتعريف enum
في ملف خاص بطريقتين
ستتعلم من المثال التالي كيف تقوم بتعريف enum
في ملف خاص بطريقتين.
فعلياً, ستتعلم كيف تستطيع إنشاء كلاس عادي و من ثم تحويله لـ enum
. و ستتعلم كيف تستطيع إنشاء enum
مباشرةً في برنامج NetBeans.
أسهل طريقة لتعريف enum
في ملف خاص
في البداية, تذكر أنه يمكنك تعريف enum
بداخل كلاس أو في ملف خارجي كما سبق و شرحنا في الدرس.
الآن لتعريف enum
في ملف خاص, يمكنك إنشاء كلاس جافا عادي, و من ثم قم فقط بتبديل كلمة class
بكلمة enum
.
مثال
هنا قمنا بإنشاء كلاس إسمه Days
.
public class Days { // class عبارة عن Days هنا }
بعد إنشاء الكلاس, قم بتبديل كلمة class
بكلمة enum
فقط كالتالي.
public enum Days { // enum عبارة عن Days هنا }
أهمية الـ enum
في حماية الكود
ستتعلم من المثال التالي أهمية الـ enum
في حماية الكود خاصةً إذا كنت تعمل ضمن فريق و تريد أن يلتزم زملاؤك بالأنواع التي يمكن إستخدامها.
في البداية, أسلوب كتابة الكود الذي ستتعلمه من المثال التالي ستتعامل معه كثيراً عند بناء تطبيقات فيها واجهة مستخدم و عند التعامل مع قواعد البيانات حيث ستتعامل مع دوال كثيرة يمكنك تمرير أنواع محددة من القيم لها.
الآن لنفترض أننا أردنا بناء لعبة يستطيع اللاعب أن يلعبها بثلاث مستويات كالتالي:
مستوى سهل ( Easy).
مستوى متوسط ( Normal).
مستوى صعب ( Hard).
الآن لكتابة كود هذه اللعبة و الذي قد يتألف من آلاف الأسطر البرمجية و التي ستكون موزعة على عدة ملفات جافا بدون القلق من مسألة كيف سيعرف كل مبرمج في الفريق كيف سيتم تحديد مستوى الصعوبة في اللعبة و لضمان أن تعرفوا جميعكم المستويات المتوفرة فيها و لتعلموا بنفس المنطق, فإنه من الأفضل أن يتم تعريف enum
واحد إسمه Level
و بداخله ثلاث ثوابت إسمها EASY
و NORMAL
و HARD
.
و عندها سيقوم كل مبرمج بكتابة الكود بناءاً على قيمة هذه الثوابت كما فعلنا في المثال التالي.
مثال
•هذا الملف مسؤول عنه مبرمج واحد و هو لتحديد المستويات المتوفرة في اللعبة.
// وضعنا فيه 3 ثوابت Level إسمه enum هنا قمنا بتعريف public enum Level { EASY, NORMAL, HARD }
•هذا الملف مسؤول عنه مبرمج واحد و يكتب فيه الرسائل التي يمكن أن تظهر في اللعبة على أساس مستوى اللعبة.
public class Messages { // لها لتحديد الرسالة التي سيتم إظهارها للمستخدم Level عند إستدعاء هذه الدالة يجب تمرير إحدى ثوابت الكلاس // لها فإنها ستظهر خطأ أمام المبرمج الذي قام باستدعائها مباشرةً أثناء كتباة الكود Level إذا لم يتم تمرير إحدى ثوابت الـ public void showPlayMessage(Level userLevel) { switch (userLevel) { case EASY: System.out.println("Play Game In Easy Mode"); break; case NORMAL: System.out.println("Play Game In normal Mode"); break; case HARD: System.out.println("Play Game In Hard Mode"); break; } } }
•هذا الملف مسؤول عنه مبرمج واحد و هو ما سيشغل اللعبة.
public class Main { public static void main(String[] args) { // لتطبع رسالة على أساسه Level.EASY مع تمرير الثابت showPlayMessage() و استدعاء الدالة Messages هنا قمنا بإنشاء كائن من الكلاس Messages msg = new Messages(); msg.showPlayMessage(Level.EASY); } }
•سنحصل على النتيجة التالية عند التشغيل.