שלום! היום נדבר על סוג נתונים מיוחד בג'אווה -
Enum
(קיצור של ספירה). מה התכונה שלהם? בואו נדמיין שאנחנו צריכים ליישם חודשים בתוכנית. נראה, מה הבעיה? אתה רק צריך לקבוע אילו נכסים יש בכל חודש. אולי צריך קודם כל את שם החודש ומספר הימים בו. הפתרון לבעיה נראה די פשוט:
public class Month {
private String name;
private int daysCount;
public Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDaysCount() {
return daysCount;
}
public void setDaysCount(int daysCount) {
this.daysCount = daysCount;
}
@Override
public String toString() {
return "Month{" +
"name='" + name + '\'' +
", daysCount=" + daysCount +
'}';
}
}
סט מלא בבקשה! יש לנו כיתה Month
, השדות הדרושים, מגדירים-מגדיר, toString()
. אלא אם כן equals()
אתה hashCode()
צריך להוסיף את זה בשביל אושר מוחלט :) עם זאת, יש לנו בעיה מושגית. כפי שאתם אולי זוכרים, אחד היתרונות העיקריים של OOP הוא בכך שהוא מקל על מודלים של ישויות בעולם האמיתי. כיסא, מכונית, כוכב לכת - קל לייצג את כל המושגים הללו מחיי היומיום בתוכנית באמצעות הפשטה. הבעיה היא שלישויות מסוימות בעולם האמיתי יש מגוון משמעויות מוגבל בהחלט. יש רק 4 עונות בשנה. יש רק 7 תווים במוזיקה. יש רק 12 חודשים בלוח השנה. לאוקיינוס יש רק 11 חברים (למרות שזה נתון לוויכוח :)) במילים אחרות, מחלקה רגילה של Java לא מסוגלת לדגמן את הישויות הללו ולכבד את האילוצים הטבעיים שלהן. בכיתה שלנו Month
יש את כל התחומים הדרושים. אבל אם מתכנת אחר ישתמש בזה, אף אחד לא ימנע ממנו ליצור אובייקטים מטורפים לחלוטין:
public class Main {
Month month1 = new Month("lolkek", 322);
Month month2 = new Month("yahoooooooooooo", 12345);
}
אם זה יופיע בתוכנית, לא יהיה קל למצוא את האשם! מצד אחד, המתכנת שיצר את האובייקטים יכול היה להבין שהשיעור Month
מתכוון ל"חודש בשנה" ולא לכתוב שטויות כאלה. מצד שני, הוא רק ניצל את ההזדמנויות שמעצב הכיתה סיפק לו. האם אני יכול להקצות שמות ומספר ימים? הוא מינה אותו. מה לעשות במצב כזה? לפני שחרורו של שפת Java גרסה 1.5, מתכנתים היו צריכים, בכנות, לצאת מזה :) באותם ימים, הם יצרו את המבנים הבאים:
public class Month {
private String name;
private int daysCount;
private Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public static Month JANUARY = new Month("Январь", 31);
public static Month FEBRUARY = new Month("Февраль", 28);
public static Month MARCH = new Month("Март", 31);
@Override
public String toString() {
return "Month{" +
"name='" + name + '\'' +
", daysCount=" + daysCount +
'}';
}
}
כאן פישטנו את מספר החודשים לשלושה במקום שנים עשר כדי שהדוגמה תהיה קצרה יותר. עיצובים כאלה אפשרו לפתור את הבעיה. האפשרויות לאובייקטים שנוצרו הוגבלו על ידי בנאי פרטי:
private Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
מתכנתים המשתמשים במחלקה לא יכלו ליצור פשוט Month
. הם נאלצו להשתמש באובייקטים סטטיים אחרונים שסופקו על ידי מפתח הכיתה. זה נראה בערך כך:
public class Main {
public static void main(String[] args) {
Month january = Month.JANUARY;
System.out.println(january);
}
}
עם זאת, מפתחי Java הבחינו בבעיה קיימת. כמובן, זה נהדר שמתכנתים הצליחו להמציא פתרון באמצעות הכלים הזמינים בשפה, אבל זה לא נראה כל כך פשוט! היה צורך בפתרון ברור, נגיש אפילו למתחילים. כך הופיע ב-Java Enum
. בעיקרו של דבר, Enum
זוהי מחלקת Java המספקת קבוצה מוגבלת של אובייקטי ערך. כך זה נראה:
public enum Month {
JANUARY,
FEBRUARY,
MARCH
}
בהגדרה ציינו שמדובר Enum
במחלקת Java, אבל האם זה באמת כך? כן, ואנחנו אפילו יכולים לבדוק את זה. נסה, למשל, לרשת את שלנו enum Month
מכיתה אחרת:
public abstract class AbstractMonth {
}
//ошибка! No extends clause allowed to enum
public enum Month extends AbstractMonth {
JANUARY,
FEBRUARY,
MARCH
}
למה זה קורה? כשאנחנו כותבים לתוכנית:
public enum Month
המהדר ממיר את הפקודה הזו לקוד כך:
public Class Month extends Enum
כפי שאתה כבר יודע, ירושה מרובה אינה מותרת ב-Java. לכן, AbstractMonth
לא יכולנו לרשת מ. Enum
כיצד ניתן להשתמש בעיצוב החדש הזה ? ובמה זה שונה מהעיצוב הישן עם static final
שדות? ובכן, למשל, העיצוב הישן לא איפשר לנו להשתמש במערכת הערכים שלנו בביטויים switch
. תארו לעצמכם שאנחנו רוצים ליצור תוכנית שתזכיר לנו אילו חגים נחוגים החודש:
public class HolidayReminder {
public void printHolidays(Month month) {
switch (month) {
//error!
case JANUARY:
}
}
}
כאן, כפי שאתה יכול לראות, המהדר זורק שגיאה. אבל אחרי ש-Java 1.5 הופיע enum
, הכל נעשה הרבה יותר פשוט:
public enum Month {
JANUARY,
FEBRUARY,
MARCH
}
public class HolidayReminder {
public void printHolidays(Month month) {
switch (month) {
case JANUARY:
System.out.println("7 января будет Рождество!");
break;
case FEBRUARY:
System.out.println("В феврале празднуется День Защитника Отечества - 23 февраля!");
break;
case MARCH:
System.out.println("В марте отмечается Всемирный Женский День - 8 марта!");
break;
}
}
}
public class Main {
public static void main(String[] args) {
HolidayReminder reminder = new HolidayReminder();
reminder.printHolidays(Month.JANUARY);
}
}
פלט מסוף:
7 января будет Рождество!
שימו לב: הגישה לאובייקטים Enum
נשארת סטטית, כפי שהייתה לפני Java 1.5. אנחנו לא צריכים ליצור אובייקט Month
כדי לגשת לחודשים. כשעובדים עם ספירות, חשוב מאוד לא לשכוח שמדובר Enum
בכיתה מן המניין. המשמעות היא שבמידת הצורך ניתן להגדיר בו בנאים ושיטות. לדוגמה, בקטע הקוד הקודם פשוט ציינו את הערכים JANUARY, FEBRUARY, MARCH. עם זאת, אנו יכולים להרחיב את שלנו enum Month
כך:
public enum Month {
JANUARY("Январь", 31),
FEBRUARY("Февраль", 28),
MARCH("Март", 31),
APRIL("Апрель", 30),
MAY("Май", 31),
JUNE("Июнь", 30),
JULY("Июль", 31),
AUGUST("Август", 31),
SEPTEMBER("Сентябрь", 30),
OCTOBER("Октябрь", 31),
NOVEMBER("Ноябрь", 30),
DECEMBER("Декабрь", 31);
private String name;
private int daysCount;
Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public static Month[] getWinterMonths() {
return new Month[]{DECEMBER, JANUARY, FEBRUARY};
}
public static Month[] getSummerMonths() {
return new Month[]{JUNE, JULY, AUGUST};
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDaysCount() {
return daysCount;
}
public void setDaysCount(int daysCount) {
this.daysCount = daysCount;
}
@Override
public String toString() {
return "Month{" +
"name='" + name + '\'' +
", daysCount=" + daysCount +
'}';
}
}
כאן הוספנו לשלנו 2 שדות enum
- שם החודש ומספר הימים, בנאי המשתמש בשדות אלו, מגדירים-מגדי, מתודה toString()
וכן 2 שיטות סטטיות. כפי שאתה יכול לראות, לא היו בעיות עם זה: כפי שאמרנו קודם, enum
זהו שיעור מן המניין:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
System.out.println(Arrays.toString(Month.getSummerMonths()));
}
}
פלט מסוף:
[Month{name='Июнь', daysCount=30}, Month{name='Июль', daysCount=31}, Month{name='Август', daysCount=31}]
לבסוף, אני רוצה להמליץ לך על ספר שימושי ביותר על ג'אווה, כלומר "ג'אווה יעילה" מאת ג'ושוע בלוך . המחבר הוא אחד היוצרים של ג'אווה, כך שאתה בהחלט יכול לסמוך על עצתו לגבי שימוש נכון ומוכשר בכלי שפה :) ביחס להרצאה שלנו, אני ממליץ לך להקדיש תשומת לב מיוחדת לפרק של הספר המוקדש ל enum
. שתהיה לך קריאה פורייה! :)
GO TO FULL VERSION