Enum ב-Java

\begin{hebrew}
סוג Enum הוזן ל-Java 1.5 כסוג חדש שבו השדות מורכבים מערך קבוע של קבועים. לדוגמה, נוכל ליצור כיוונים בתור Enum ב-Java עם שדות קבועים כמו EAST, WEST, NORTH ו-SOUTH.

Enum ב-Java

במדריך זה, נלמד כיצד ליצור Enum. נתעקוב גם אחר היתרונות של שימוש ב-enums ב-Java ובמאפיינים של סוגי enum. נלמד גם על שימוש ב-Java Enum ב־valueOf, enum values, EnumSet ו־EnumMap עם דוגמאות.

דוגמה ל-Enum ב-Java

המילה enum ב-Java משמשת ליצירת סוג Enum. בואו נציץ על דוגמה לתוכנית Enum ב-Java.

package com.journaldev.enums;

public enum ThreadStates {
	START,
	RUNNING,
	WAITING,
	DEAD;
}

בדוגמה למעלה, ThreadStates הוא סוג Enum עם שדות קבועים כמו START, RUNNING, WAITING ו-DEAD.

Enum ב-Java נגד קבועים

כעת בואו נראה כיצד סוג Enum ב-Java טוב יותר משדות קבועים רגילים במחלקות Java. בואו ניצור מחלקת קבועים דומה ב-Java.
\end{hebrew}

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 וקבועים משתמשים בתוכנית Java:

/**
* This method shows the benefit of using Enum over Constants
*/
private static void benefitsOfEnumOverConstants() {
	//ערכי Enum קבועים
	simpleEnumExample(ThreadStates.START);
	simpleEnumExample(ThreadStates.WAITING);
	simpleEnumExample(ThreadStates.RUNNING);
	simpleEnumExample(ThreadStates.DEAD);
	simpleEnumExample(null);
		
	simpleConstantsExample(1);
	simpleConstantsExample(2);
	simpleConstantsExample(3);
	simpleConstantsExample(4);
	//אנו יכולים להעביר כל קבוע int
	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.

  1. אנו יכולים להעביר כל קבוע int לשיטת simpleConstantsExample אך יש לנו אפשרות להעביר רק ערכים קבועים ל- simpleEnumExample, ולכן הוא מספק בטיחות סוג.
  2. ניתן לשנות את ערכי הקבוע int במחלקת ThreadStatesConstant אך התוכנית לעיל לא תזרוק חריגה כלשהי. התוכנית שלנו יכולה לא לעבוד כמצופה אך אם נשנה את ערכי ה-enum, נקבל שגיאת זמן קומפילציה שתסיר כל אפשרות לבעיות בזמן ריצה.

שיטות Enum ב-Java

עכשיו נראה יותר תכונות של enum ב-Java עם דוגמה.

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();
	//בני Enum צריכים להיות תמיד פרטיים
	private ThreadStatesEnum(int i){
		priority = i;
	}
	
	//Enum יכול להכיל שיטות
	public int getPriority(){
		return this.priority;
	}
	
	public void setPriority(int p){
		this.priority = p;
	}
	
	//Enum יכול לדרוס פונקציות
	@Override
	public String toString(){
		return "Default ThreadStatesConstructors implementation. Priority="+getPriority();
	}

	@Override
	public void close() throws IOException {
		System.out.println("Close of Enum");
	}
}

נקודות חשובות ב-Enum ב-Java

למטה יש כמה מהנקודות החשובות עבור Enums ב-Java.

  1. כל enum ב-Java מרחיב מכריח את המחלקה java.lang.Enum שמרחיבה את מחלקת Object ומיישמת את הממשקים Serializable ו־Comparable. לכן אי אפשר להרחיב מחלקה אחרת ב־enum.
  2. מאחר ש־enum הוא מילת מפתח, אי אפשר לסיים שם חבילה עם זה, לדוגמה com.journaldev.enum אינו שם חבילה חוקי.
  3. Enum יכול ליישם ממשקים. כפי שנראה בדוגמה לעיל, הוא מיישם את הממשק Closeable.
  4. בנאי enum תמיד הם פרטיים.
  5. אי אפשר ליצור מופע של enum באמצעות אופרטור new.
  6. אפשר להגדיר שיטות מופשטות ב-Java enum, ואז כל השדות של enum חייבים ליישם את השיטה המופשטת. בדוגמה לעיל, getDetail() היא השיטה המופשטת וכל שדות הenum מיישמים אותה.
  7. אפשר להגדיר שיטה ב־enum ושדות הenum יכולים לדרוס אותם גם כן. לדוגמה, השיטה toString() מוגדרת ב־enum והשדה enum START דרס אותה.
  8. לשדות enum ב-Java יש שם מרחב, ואפשר להשתמש בשדה enum רק עם שם המחלקה כמו ThreadStates.START.
  9. ניתן להשתמש ב-enums ב־הצהרת switch, נראה זאת בפעולה בחלק האחר של המדריך.
  10. אנו יכולים להרחיב על enum הקיימים מבלי לשבור פונקציונליות קיימת. לדוגמה, ניתן להוסיף שדה חדש NEW ב-enum ThreadStates מבלי להשפיע על פונקציונליות קיימת כלשהי.
  11. מאחר ושדות enum הם קבועים, המומלץ ביותר ב-Java הוא לכתוב אותם באותיות גדולות ולהשתמש בקו תחתון במקום רווח. לדוגמה EAST, WEST, EAST_DIRECTION וכו'
  12. קבועי enum הם סטטיים וקבועים באופן משמעי
  13. קבועי enum הם קבועים אך ערכם יכול להשתנות. לדוגמה, ניתן להשתמש במתודת setPriority() כדי לשנות את עדיפות הקבועים enum. נראה זאת בדוגמה למטה.
  14. מאחר וקבועי enum הם קבועים, אנו יכולים להשוות בטוח ביניהם באמצעות השוואת "==" ושיטות equals(). שתי השיטות יציגו אותו תוצאה.

Java EnumSet, EnumMap, valueOf()

עכשיו שיש לנו מושג על רוב התכונות של Enum, בואו נסתכל על דוגמה לתוכנית Enum ב-Java. לאחר מכן נלמד עוד כמה תכונות של enum.

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();
	}

}

לפני שנסביר תכונות חשובות נוספות של enum, בואו נראה את הפלט של התוכנית למעלה.

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

נקודות חשובות

  1. השיטה usingEnumMethods() מדגימה איך ליצור אובייקט enum ואיך ניתן להשתמש בשיטות שלו. היא מדגימה גם את השימוש בשיטה setPriority(int i) לשינוי המשתנה של האינום.
  2. השיטה usingEnumValueOf() מדגימה את השימוש בשיטה java.util.Enum valueOf(enumType, name) דרך הקוד שבו ניתן ליצור אובייקט enum מתוך מחרוזת. היא מזריקה IllegalArgumentException אם הסוג enum המצויין אינו מכיל קבוע עם השם המצויין, או אם אובייקט הסוג שהוצג אינו מייצג סוג enum. היא מזריקה גם NullPointerException אם אחד מהארגומנטים הוא null.
  3. השיטה usingEnumValues() מדגימה את השימוש בשיטה values() שמחזירה מערך המכיל את כל הערכים של האינום בסדר שהם מוגדרים. שיטה זו נוצרת אוטומטית על ידי מהדר ה-Java לכל enum. לא תמצאו את המימוש של values() במחלקת java.util.Enum.
  4. השיטה usingEnumInSwitch() מדגימה איך להשתמש בקבועי enum במקרה switch.
  5. השיטה usingEnumMap() מדגימה את השימוש ב־java.util.EnumMap, שנכנס לשימוש במסגרת מסגרת האוסף של Java 1.5. EnumMap הוא מימוש של Map לשימוש עם מפתחות מסוג enum. כל המפתחות במפתח enum חייבים להגיע מסוג enum אחד המוגדר, באופן מפורש או משתמש, בעת יצירת המפה. אי אפשר להשתמש ב־null כמפתח עבור EnumMap ו־EnumMap אינו מסונכרוני.
  6. usingEnumSet() השיטה מציגה שימוש של java.util.EnumSet, שהוא מימוש של Set לשימוש עם סוגי enum. כל האיברים באוסף enum חייבים להיות מסוג enum יחיד שמוגדר, באופן מפורש או באופן משולי, כאשר האוסף נוצר. EnumSet אינו מסונכרן ואיברים null אינם מותרים. כמו כן, הוא מספק כמה שיטות שימושיות כמו copyOf(Collection<E> c), of(E first, E... rest) ו- complementOf(EnumSet<E> s).

ניתן לבדוק את כל הדוגמאות מתוך מאגר הקוד ב-GitHub.

התייחסות: מסמך Oracle

Source:
https://www.digitalocean.com/community/tutorials/java-enum