здрасти Днес ще говорим за един от специалните типове данни на Java:
Enum
(съкратено от „изброяване“). Какво го прави специален? Нека си представим Howво ни трябва, за да внедрим „месеци“ в една програма. Не изглежда проблемно, нали? Просто трябва да определим Howви свойства има всеки месец. Може би първо се нуждаем от името на месеца и броя на дните в него. Решението изглежда доста просто:
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
клас, задължителните полета, getter/setter и toString()
. Освен ако, разбира се, не трябва да добавим equals()
иhashCode()
за постигане на пълно щастие :) Но тук имаме концептуален проблем. Както вероятно си спомняте, едно от основните предимства на ООП е, че улеснява моделирането на обекти от реалния свят. Стол, кола, планета - всички тези концепции от обикновения живот лесно се представят в програма с помощта на абстракция. Проблемът е, че някои обекти от реалния свят имат строго ограничен диапазон от стойности. Има само 4 сезона в годината. Има само 8 ноти в една октава. Календарът има само 12 месеца. И Дани Оушън от 11 на Оушън има само 11 приятели (въпреки че това няма meaning :)) Това, което има meaning е, че един обикновен Java клас не е в състояние да моделира тези обекти и да наложи естествените им ограничения. НашитеMonth
има всички задължителни полета. Но ако друг програмист го използва, никой не може да го спре да създава напълно безумни обекти:
public class Main {
Month month1 = new Month("lolkek", 322);
Month month2 = new Month("yahoooooooooooo", 12345);
}
Ако това се появи в нашия code, няма да е лесно да открием виновника! От една страна, програмистът, който създава обектите, може да осъзнае, че класът 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("January", 31);
public static Month FEBRUARY = new Month("February", 28);
public static Month MARCH = new Month("March", 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 обърнаха внимание на съществуващия проблем. Разбира се, страхотно е, че програмистите успяха да измислят решение, използвайки инструментите, налични в езика, но не изглежда много лесно! Беше необходимо очевидно решение, дори за новаци. И така Enum
се появи в Java. По принцип Enum
е Java клас, който предоставя ограничен набор от обектни стойности. Ето How изглежда:
public enum Month {
JANUARY,
FEBRUARY,
MARCH
}
В дефиницията посочихме, че това Enum
е Java клас, но наистина ли е вярно? Да, и дори можем да го проверим. Например, опитайте да накарате нашия Month
enum да наследи няHowъв друг клас:
public abstract class AbstractMonth {
}
// Error! The extends clause cannot be used with an enum
public enum Month extends AbstractMonth {
JANUARY,
FEBRUARY,
MARCH
}
защо става така Когато пишем:
public enum Month
компилаторът преобразува този оператор в следния code:
public Class Month extends Enum
Както вече знаете, Java не поддържа множествено наследяване. Следователно не можем да наследим AbstractMonth
. Enum
Как може да се използва тази нова конструкция ? И How се различава от старата конструкция с static final
полета? Е, като пример, старата конструкция не ни позволяваше да използваме собствен набор от стойности в switch
изявления. Представете си, че искаме да създадем програма, която да ни напомня за празниците, празнувани всеки месец:
public class HolidayReminder {
public void printHolidays(Month month) {
switch (month) {
// Error!
case JANUARY:
}
}
}
Както можете да видите, компилаторът извежда грешка тук. Но след като enum
се появи в Java 1.5, всичко стана много по-просто:
public enum Month {
JANUARY,
FEBRUARY,
MARCH
}
public class HolidayReminder {
public void printHolidays(Month month) {
switch (month) {
case JANUARY:
System.out.println("New Year's Day is January 1st!");
break;
case FEBRUARY:
System.out.println("Valentine's Day is February 14th!");
break;
case MARCH:
System.out.println("Saint Patrick's Day is March 17th!");
break;
}
}
}
public class Main {
public static void main(String[] args) {
HolidayReminder reminder = new HolidayReminder();
reminder.printHolidays(Month.JANUARY);
}
}
Конзолен изход:
New Year's Day is January 1st!
Имайте предвид, че достъпът до Enum
обекти остава статичен, точно Howто беше преди Java 1.5. Не е необходимо да създаваме Month
обект за достъп до месеците. Когато работите с enum, е много важно да не забравяте, че това Enum
е пълноценен клас. Това означава, че при необходимост можете да дефинирате конструктори и методи в него. Например в предишния codeов фрагмент ние просто посочихме стойностите: ЯНУАРИ, ФЕВРУАРИ, МАРТ. Въпреки това можем да разширим нашия Month
enum по следния начин:
public enum Month {
JANUARY("January", 31),
FEBRUARY("February", 28),
MARCH("March", 31),
APRIL("April", 30),
MAY("May", 31),
JUNE("June", 30),
JULY("July", 31),
AUGUST("August", 31),
SEPTEMBER("September", 30),
OCTOBER("October", 31),
NOVEMBER("November", 30),
DECEMBER("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 +
'}';
}
}
Тук дадохме нашите enum
2 полета (името на месеца и броя на дните), конструктор, който използва тези полета, getter/setter, метода 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='June', daysCount=30}, Month{name='July', daysCount=31}, Month{name='August', daysCount=31}]
И накрая, искам да препоръчам една изключително полезна книга за Java, а именно „Ефективна Java“ от Джошуа Блок . Authorът е един от създателите на Java, така че определено можете да се доверите на съветите му How правилно и компетентно да използвате инструментите на езика :) По отношение на нашия урок ви препоръчвам да обърнете специално внимание на главата на книгата за Enum
. Приятно четене! :)