تم إدخال Enum في Java 1.5 كنوع جديد يتألف حقوله من مجموعة ثابتة من الثوابت. على سبيل المثال، يمكننا إنشاء اتجاهات كـ Java Enum بحقول ثابتة مثل EAST وWEST وNORTH وSOUTH.
Java Enum
في هذا البرنامج التعليمي، سنتعلم كيفية إنشاء Enum. سنلقي أيضًا نظرة على فوائد استخدام enums في Java وميزات أنواع الـ enum. سنتعلم أيضًا استخدام Java Enum
valueOf
و enum values
و EnumSet
و EnumMap
مع أمثلة.
مثال Java Enum
تستخدم كلمة مفتاحية enum في Java لإنشاء نوع enum. دعونا نلقي نظرة على برنامج مثالي لـ Java enum.
package com.journaldev.enums;
public enum ThreadStates {
START,
RUNNING,
WAITING,
DEAD;
}
في المثال أعلاه، ThreadStates هو enum مع حقول ثابتة تتألف من START وRUNNING وWAITING وDEAD.
Java Enum مقابل الثوابت
الآن دعونا نرى كيف يكون enum في Java أفضل من حقول الثوابت العادية في فئات Java. دعونا نقم بإنشاء فئة ثوابت مماثلة في Java.
package com.journaldev.enums;
public class ThreadStatesConstant {
public static final int START = 1;
public static final int WAITING = 2;
public static final int RUNNING = 3;
public static final int DEAD = 4;
}
الآن دعونا نرى كيف يتم استخدام كل من القيم المحددة (enum) والثوابت في برنامج جافا:
/**
* This method shows the benefit of using Enum over Constants
*/
private static void benefitsOfEnumOverConstants() {
//قيم القيم المحددة ثابتة
simpleEnumExample(ThreadStates.START);
simpleEnumExample(ThreadStates.WAITING);
simpleEnumExample(ThreadStates.RUNNING);
simpleEnumExample(ThreadStates.DEAD);
simpleEnumExample(null);
simpleConstantsExample(1);
simpleConstantsExample(2);
simpleConstantsExample(3);
simpleConstantsExample(4);
//يمكننا تمرير أي ثابت عددي
simpleConstantsExample(5);
}
private static void simpleEnumExample(ThreadStates th) {
if(th == ThreadStates.START) System.out.println("Thread started");
else if (th == ThreadStates.WAITING) System.out.println("Thread is waiting");
else if (th == ThreadStates.RUNNING) System.out.println("Thread is running");
else System.out.println("Thread is dead");
}
private static void simpleConstantsExample(int i) {
if(i == ThreadStatesConstant.START) System.out.println("Thread started");
else if (i == ThreadStatesConstant.WAITING) System.out.println("Thread is waiting");
else if (i == ThreadStatesConstant.RUNNING) System.out.println("Thread is running");
else System.out.println("Thread is dead");
}
إذا نظرنا إلى المثال أعلاه، لدينا مخاطرتان مع استخدام الثوابت تُحل بواسطة القيم المحددة (enum).
- يمكننا تمرير أي ثابت عددي إلى الطريقة
simpleConstantsExample
ولكن يمكننا تمرير قيم ثابتة فقط إلى simpleEnumExample، لذلك فهي توفر الأمان النوعي. - يمكننا تغيير قيمة الثوابت العددية في فئة
ThreadStatesConstant
لكن البرنامج أعلاه لن يُظهر أي استثناء. قد لا يعمل برنامجنا كما هو متوقع ولكن إذا قمنا بتغيير ثوابت القيم المحددة (enum)، سنحصل على خطأ وقت التجميع الذي يزيل أي إمكانية لمشاكل وقت التشغيل.
طرق القيم المحددة في جافا
الآن دعونا نرى المزيد من ميزات القيم المحددة في جافا مع مثال.
package com.journaldev.enums;
import java.io.Closeable;
import java.io.IOException;
/**
* This Enum example shows all the things we can do with Enum types
*
*/
public enum ThreadStatesEnum implements Closeable{
START(1){
@Override
public String toString(){
return "START implementation. Priority="+getPriority();
}
@Override
public String getDetail() {
return "START";
}
},
RUNNING(2){
@Override
public String getDetail() {
return "RUNNING";
}
},
WAITING(3){
@Override
public String getDetail() {
return "WAITING";
}
},
DEAD(4){
@Override
public String getDetail() {
return "DEAD";
}
};
private int priority;
public abstract String getDetail();
//يجب أن تكون مُنشئات القيم المحددة خاصة دائمًا.
private ThreadStatesEnum(int i){
priority = i;
}
//يمكن للقيم المحددة أن تحتوي على طرق
public int getPriority(){
return this.priority;
}
public void setPriority(int p){
this.priority = p;
}
//يمكن للقيم المحددة أن تعدل الوظائف
@Override
public String toString(){
return "Default ThreadStatesConstructors implementation. Priority="+getPriority();
}
@Override
public void close() throws IOException {
System.out.println("Close of Enum");
}
}
نقاط مهمة عن القيم المحددة في جافا
فيما يلي بعض النقاط المهمة للقيم المحددة في جافا.
- تعتمد جميع تعريفات الـ Java enum ضمنيًا على فئة
java.lang.Enum
التي تمتد إلى فئة Object وتنفذ واجهتي Serializable و Comparable. لذلك لا يمكننا تمديد أي فئة في enum. - نظرًا لأن enum هو كلمة محجوزة، فإنه لا يمكننا استخدامها في نهاية اسم الحزمة، على سبيل المثال
com.journaldev.enum
ليس اسمًا صحيحًا لحزمة. - يمكن للـ Enum تنفيذ واجهات. كما هو موضح في مثال الـ enum أعلاه، حيث يقوم بتنفيذ واجهة
Closeable
. - تكون مُنشئات الـ Enum دائمًا خاصة.
- لا يمكننا إنشاء مثيل للـ enum باستخدام مشغل new.
- يمكننا أن نعلن طرقًا مجردة في جافا enum، وبهذا يجب على جميع حقول الـ enum تنفيذ الطريقة المجردة. في المثال أعلاه،
getDetail()
هي الطريقة المجردة وقد قامت جميع حقول الـ enum بتنفيذها. - يمكننا تعريف طريقة في الـ enum ويمكن لحقول الـ enum تجاوزها أيضًا. على سبيل المثال، تم تعريف طريقة
toString()
في الـ enum وقد قامت حقل START بتجاوزها. - تحتوي حقول جافا enum على مساحة أسماء، يمكننا استخدام حقل الـ enum فقط باستخدام اسم الفئة مثل
ThreadStates.START
- يمكن استخدام Enums في بيان التحويل، سنرى ذلك في الجزء اللاحق من هذا البرنامج التعليمي.
- يمكننا تمديد التعريف التحصيلي الحالي دون كسر أي وظائف موجودة بالفعل. على سبيل المثال، يمكننا إضافة حقل جديد NEW في التعريف التحصيلي لحالات الخيوط بدون التأثير على أي وظائف موجودة بالفعل.
- نظرًا لأن حقول التعريف التحصيلي هي ثوابت، فإن الممارسة الأفضل في جافا هي كتابتها بأحرف كبيرة مع استخدام شرطة سفلية للفراغات. على سبيل المثال EAST، WEST، EAST_DIRECTION الخ.
- تعتبر ثوابت التعريف التحصيلي ضمنيًا ثابتة ونهائية
- ثوابت التعريف التحصيلي نهائية ولكن يمكن تغيير قيمتها. على سبيل المثال، يمكننا استخدام الطريقة
setPriority()
لتغيير أولوية ثوابت التعريف التحصيلي. سنرى ذلك في الاستخدام في المثال أدناه. - نظرًا لأن ثوابت التعريف التحصيلي نهائية، يمكننا مقارنتها بأمان باستخدام “==” وأساليب equals(). ستعطي كلاهما نفس النتيجة.
Java EnumSet، EnumMap، valueOf()
الآن نعرف معظم ميزات التعريف التحصيلي، دعنا نلقي نظرة على برنامج مثالي لتعريف التحصيلي في جافا. ثم سنتعلم بعض الميزات الإضافية لتعريف التحصيلي.
package com.journaldev.enums;
import java.io.IOException;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Set;
public class JavaEnumExamples {
public static void main(String[] args) throws IOException {
usingEnumMethods();
usingEnumValueOf();
usingEnumValues();
usingEnumInSwitch(ThreadStatesEnum.START);
usingEnumInSwitch(ThreadStatesEnum.DEAD);
usingEnumMap();
usingEnumSet();
}
private static void usingEnumSet() {
EnumSet enumSet = EnumSet.allOf(ThreadStatesEnum.class);
for(ThreadStatesEnum tsenum : enumSet){
System.out.println("Using EnumSet, priority = "+tsenum.getPriority());
}
}
private static void usingEnumMap() {
EnumMap<ThreadStatesEnum, String> enumMap = new EnumMap<ThreadStatesEnum,String>(ThreadStatesEnum.class);
enumMap.put(ThreadStatesEnum.START, "Thread is started");
enumMap.put(ThreadStatesEnum.RUNNING, "Thread is running");
enumMap.put(ThreadStatesEnum.WAITING, "Thread is waiting");
enumMap.put(ThreadStatesEnum.DEAD, "Thread is dead");
Set keySet = enumMap.keySet();
for(ThreadStatesEnum key : keySet){
System.out.println("key="+key.toString()+":: value="+enumMap.get(key));
}
}
private static void usingEnumInSwitch(ThreadStatesEnum th) {
switch (th){
case START:
System.out.println("START thread");
break;
case WAITING:
System.out.println("WAITING thread");
break;
case RUNNING:
System.out.println("RUNNING thread");
break;
case DEAD:
System.out.println("DEAD thread");
}
}
private static void usingEnumValues() {
ThreadStatesEnum[] thArray = ThreadStatesEnum.values();
for(ThreadStatesEnum th : thArray){
System.out.println(th.toString() + "::priority="+th.getPriority());
}
}
private static void usingEnumValueOf() {
ThreadStatesEnum th = Enum.valueOf(ThreadStatesEnum.class, "START");
System.out.println("th priority="+th.getPriority());
}
private static void usingEnumMethods() throws IOException {
ThreadStatesEnum thc = ThreadStatesEnum.DEAD;
System.out.println("priority is:"+thc.getPriority());
thc = ThreadStatesEnum.DEAD;
System.out.println("Using overriden method."+thc.toString());
thc = ThreadStatesEnum.START;
System.out.println("Using overriden method."+thc.toString());
thc.setPriority(10);
System.out.println("Enum Constant variable changed priority value="+thc.getPriority());
thc.close();
}
}
قبل شرح الميزات الهامة الأخرى للتعريف التحصيلي، دعونا نرى نتائج البرنامج أعلاه.
priority is:4
Using overriden method.Default ThreadStatesConstructors implementation. Priority=4
Using overriden method.START implementation. Priority=1
Enum Constant variable changed priority value=10
Close of Enum
th priority=10
START implementation. Priority=10::priority=10
Default ThreadStatesConstructors implementation. Priority=2::priority=2
Default ThreadStatesConstructors implementation. Priority=3::priority=3
Default ThreadStatesConstructors implementation. Priority=4::priority=4
START thread
DEAD thread
key=START:: value=Thread is started
key=RUNNING:: value=Thread is running
key=WAITING:: value=Thread is waiting
key=DEAD:: value=Thread is dead
Using EnumSet, priority = 10
Using EnumSet, priority = 2
Using EnumSet, priority = 3
Using EnumSet, priority = 4
نقاط مهمة
- الأسلوب
usingEnumMethods()
يوضح كيفية إنشاء كائن enum وكيفية استخدام أساليبه. كما يوضح استخدام الأسلوبsetPriority(int i)
لتغيير متغير ال enum. - الأسلوب
usingEnumValueOf()
يوضح استخدامjava.util.Enum
valueOf(enumType, name)
الذي يمكننا من إنشاء كائن enum من نص. يرمي إلىIllegalArgumentException
إذا لم يكن النوع enum المحدد يحتوي على ثابت بالاسم المحدد، أو إذا لم يمثل كائن الفئة المحددة نوع enum. كما يرمي إلىNullPointerException
إذا كان أي من الوسيطات فارغًا. - الأسلوب
usingEnumValues()
يوضح استخدام الأسلوب values() الذي يعيد مصفوفة تحتوي على جميع قيم ال enum في الترتيب الذي تم تعريفه به. يتم إنشاء هذا الأسلوب تلقائيًا من قبل مترجم جافا لكل enum. لن تجد تنفيذًا للأسلوب values() في فئةjava.util.Enum
. - الأسلوب
usingEnumInSwitch()
يوضح كيفية استخدام الثوابت enum في switch case. - الأسلوب
usingEnumMap()
يوضح استخدام java.util.EnumMap، الذي تم إدخاله في إطار عمل مجموعات جافا 1.5.EnumMap
هو تنفيذ Map للاستخدام مع مفاتيح enum. يجب أن تكون جميع المفاتيح في خريطة enum من نوع enum واحد محدد، صراحة أو ضمنياً، عند إنشاء الخريطة. لا يمكن استخدام قيمة فارغة كمفتاح لـ EnumMap وليست EnumMap متزامنة. usingEnumSet()
الأسلوب يظهر استخدام java.util.EnumSet، الذي هو تنفيذ للمجموعة (Set) للاستخدام مع أنواع التعداد (enum). يجب أن تأتي جميع العناصر في مجموعة التعداد من نوع تعداد واحد يُحدد صراحة أو ضمناً عند إنشاء المجموعة. تنفيذ EnumSet غير متزامن ولا تُسمح بالعناصر الفارغة. كما يوفر بعض الأساليب المفيدة مثلcopyOf(Collection<E> c)
،of(E first, E... rest)
وcomplementOf(EnumSet<E> s)
.
يمكنك فحص جميع الأمثلة من مستودع GitHub لدينا.
المرجع: مستند أوراكل
Source:
https://www.digitalocean.com/community/tutorials/java-enum