Hei! I dag skal vi snakke om en av Javas spesielle datatyper:
Virker ikke problematisk, ikke sant? Vi trenger bare å finne ut hvilke egenskaper den ene måneden har. Kanskje trenger vi først navnet på måneden og antall dager i den. Løsningen ser ganske enkel ut:
Forfatteren er en av Javas skapere, så du kan definitivt stole på hans råd om hvordan du bruker språkets verktøy riktig og kompetent :) Når det gjelder leksjonen vår, anbefaler jeg at du legger spesielt merke til bokens kapittel om
Enum
(forkortelse for "enumeration"). Hva gjør det spesielt? La oss forestille oss hva vi trenger for å implementere "måneder" i et program. 
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 +
'}';
}
}
Hele sjabben! Vi har en Month
klasse, de obligatoriske feltene, getter/settere og toString()
. Med mindre vi selvfølgelig må legge til equals()
oghashCode()
å oppnå fullstendig lykke :) Men her har vi et konseptuelt problem. Som du sikkert husker, er en av hovedfordelene med OOP at det gjør det enkelt å modellere enheter fra den virkelige verden. En stol, en bil, en planet — alle disse konseptene fra det vanlige livet er lett representert i et program ved hjelp av abstraksjon. Problemet er at enkelte enheter i den virkelige verden har et strengt begrenset verdiområde. Det er bare 4 sesonger i løpet av et år. Det er bare 8 toner i en oktav. Kalenderen har kun 12 måneder. Og Danny Ocean of Ocean's 11 har bare 11 venner (selv om dette ikke spiller noen rolle :)) Det som betyr noe er at en vanlig Java-klasse ikke er i stand til å modellere disse enhetene og håndheve deres naturlige begrensninger. VårMonth
klasse har alle de obligatoriske feltene. Men hvis en annen programmerer bruker det, kan ingen stoppe ham eller henne fra å lage helt sinnsyke objekter:
public class Main {
Month month1 = new Month("lolkek", 322);
Month month2 = new Month("yahoooooooooooo", 12345);
}
Hvis dette vises i koden vår, vil det ikke være lett å finne den skyldige! På den ene siden kan programmereren som lager objektene innse at klassen Month
betyr "måned i et år" og ikke skrive slikt tull. På den annen side utnytter programmereren bare evner som klassedesigneren ga. Er det mulig å tildele vilkårlige navn og antall dager? Det er akkurat det vi fikk. Hva bør vi da gjøre i denne situasjonen? Ærlig talt, før Java 1.5 ble utgitt, måtte programmerere være kreative :) På den tiden laget de strukturer som dette:
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 +
'}';
}
}
Her har vi kuttet antall måneder fra tolv til tre for å gjøre eksemplet kortere. Slike design gjorde det mulig å løse problemet. Muligheten til å lage objekter var begrenset til en privat konstruktør:
private Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
Programmerere som bruker klassen kunne ikke bare lage Month
objekter. De måtte bruke de endelige statiske objektene levert av klasseutvikleren. For eksempel slik:
public class Main {
public static void main(String[] args) {
Month january = Month.JANUARY;
System.out.println(january);
}
}
Men Java-utviklere trakk oppmerksomheten til det eksisterende problemet. Selvfølgelig er det flott at programmerere klarte å komme opp med en løsning ved å bruke verktøyene som er tilgjengelige på språket, men det ser ikke veldig enkelt ut! En åpenbar løsning var nødvendig, selv for nybegynnere. Og så Enum
dukket det opp i Java. I utgangspunktet Enum
er det en Java-klasse som gir et begrenset sett med objektverdier. Slik ser det ut:
public enum Month {
JANUARY,
FEBRUARY,
MARCH
}
I definisjonen indikerte vi at det Enum
er en Java-klasse, men er det virkelig sant? Ja, og vi kan til og med bekrefte det. For eksempel, prøv å få Month
enumen vår til å arve en annen klasse:
public abstract class AbstractMonth {
}
// Error! The extends clause cannot be used with an enum
public enum Month extends AbstractMonth {
JANUARY,
FEBRUARY,
MARCH
}
Hvorfor skjer det? Når vi skriver:
public enum Month
kompilatoren konverterer denne setningen til følgende kode:
public Class Month extends Enum
Som du allerede vet, støtter ikke Java multippel arv. Derfor kan vi ikke arve AbstractMonth
. Hvordan kan denne nye konstruksjonen Enum
brukes? Og hvordan skiller den seg fra den gamle konstruksjonen med static final
felt? Vel, som et eksempel, den gamle konstruksjonen lot oss ikke bruke vårt eget sett med verdier i switch
utsagn. Tenk deg at vi ønsker å lage et program som minner oss om høytidene som feires hver måned:
public class HolidayReminder {
public void printHolidays(Month month) {
switch (month) {
// Error!
case JANUARY:
}
}
}
Som du kan se, kaster kompilatoren en feil her. Men når du først enum
dukket opp i Java 1.5, ble alt mye enklere:
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);
}
}
Konsoll utgang:
New Year's Day is January 1st!
Merk at tilgangen til Enum
objekter forble statisk, akkurat som før Java 1.5. Vi trenger ikke opprette et Month
objekt for å få tilgang til månedene. Når du jobber med enums, er det veldig viktig å ikke glemme at det Enum
er en fullverdig klasse. Dette betyr at du om nødvendig kan definere konstruktører og metoder i den. For eksempel, i forrige kodefragment spesifiserte vi ganske enkelt verdiene: JANUAR, FEBRUAR, MARS. Vi kan imidlertid utvide Month
opptellingen vår slik:
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 +
'}';
}
}
Her ga vi våre enum
2 felter (navnet på måneden og antall dager), en konstruktør som bruker disse feltene, getter/settere, metoden toString()
og 2 statiske metoder. Som du ser var det ingen problemer med dette. Igjen, Enum
er virkelig en fullverdig klasse:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
System.out.println(Arrays.toString(Month.getSummerMonths()));
}
}
Konsoll utgang:
[Month{name='June', daysCount=30}, Month{name='July', daysCount=31}, Month{name='August', daysCount=31}]
Til slutt vil jeg anbefale en ekstremt nyttig Java-bok, nemlig «Effektiv Java» av Joshua Bloch . 
Enum
. God lesning! :)
GO TO FULL VERSION