Enum i Java 1.5
Förord
I den här artikeln ska jag förklara hur språkkonstruktionen enum fungerar i Java 1.5. Du kan läsa om andra nyheter i Java 1.5 i min blogg.Innehåll
»»
»
»
Relaterade artiklar
Vad är enum?
Vad är enum?
I den här artikeln ska jag förklara hur språkkonstruktionen enum fungerar i Java 1.5. Den första delen av artikeln innehåller all information man behöver för att kunna använda enum på ett bra sätt. Den andra delen innehåller allt man behöver veta för att kunna använda enum på ett dåligt sätt, eller med andra ord, spännande detaljer om hur man kan använda enum för att förvirra sig själv och andra. Men låt oss börja med det första.
"Enum" är en förkortning av "enumeration" som betyder "uppräkning", och en uppräkning är precis vad det är. Enum är en konstruktion som gör det möjligt att skapa nya typer som representerar uppräkningar av saker. Låt oss ta ett exempel. Hippokrates ska skapa ett personregister för medicinskt bruk och han behöver därför en uppräkning av de fyra temperament personer kan ha. Han deklarerar Temperament som en enum-typ så här:
enum Temperament { MELANKOLIKER, SANGVINIKER, FLEGMATIKER, KOLERIKER }
När väl detta är gjort kan variabler deklareras att vara av typen Temperament och sådana variabler kan bara innehålla något av de fyra uppräknade värdena (melankoliker, sangviniker, flegmatiker eller koleriker). Hippokrates skriver en person-klass:
class Person {
public String namn;
public Temperament temperament;
}
För person-objekt kan temperamentet anges:
arneAnka.temperament = Temperament.KOLERIKER;
Enum passar bra in i typteorin som säger att en typ är en mängd värden. Att deklarera en variabel att vara av en viss typ säger att variabeln kan innehålla något av de värden som typen innehåller. Typen boolean innehåller värdena true och false vilket gör att en variabel av typen boolean måste ha just ett av dessa två värden. Typen int innehåller alla heltal från -2147483648 till 2147483647 vilket gör att en sådan variabel måste innehålla något av dessa värden. Enum-typen Temperament innehåller de fyra temperamenten, och en temperament-variabel är därför begränsad till dessa.
I den mörka tiden, före Java 1.5, hade Hippokrates tvingats använda heltalskonstanter för att lösa samma problem. Exemplet ovan hade sett ut så här:
class Temperament {
public static final int MELANKOLIKER = 0;
public static final int SANGVINIKER = 1;
public static final int FLEGMATIKER = 2;
public static final int KOLERIKER = 3;
}
class Person {
String namn;
int temperament;
}
arneAnka.temperament = Temperament.KOLERIKER;
göranPersson.temperament = 19; // Vad betyder detta?
Det största problemet med att använda heltalskonstanter istället för en enum-typ är att kompilatorn inte kan begränsa vilka heltal som variabler ska få innehålla. I vårt exempel bör ju bara värdena 0, 1, 2 och 3 få användas. Man brukar säga att det hela inte är typsäkert. Det går inte heller att få några vettiga utskrifter av sådana här konstanter. Det som skrivs ut är konstantens heltalsvärde:
System.out.println("Arnes temperament: " + arneAnka.temperament); // Utskriften blir 3
Utskrifter av enum
Utskrifter av enum
enum Veckodag { MÅNDAG, TISDAG, ONSDAG, TORSDAG, FREDAG, LÖRDAG, SÖNDAG }
class Test {
public static void main(String[] args) {
for(Veckodag v : Veckodag.values()) {
System.out.println("Dag: " + v);
}
}
}
Utskriften blir:
Dag: MÅNDAG
Dag: TISDAG
Dag: ONSDAG
Dag: TORSDAG
Dag: FREDAG
Dag: LÖRDAG
Dag: SÖNDAG
For-loopen fungerar tack vare att metoden values returnerar en array av elementen i den ordning som de är uppräknade i enum-typen Veckodag. Och arrayen fungerar naturligtvis utmärkt tillsammans med den nya for-loopen i Java 1.5. Utskriften fungerar tack vare att det finns en speciell toString-metod för enum-värden. Det går också att använda switch-konstruktionen tillsammans med enum-typer. Förut fungerade switch bara tillsammans med heltalstyper.
Veckodag v = favoritdag();
switch(v) {
case MÅNDAG:
System.out.println("Det var ovanligt");
break;
case LÖRDAG:
System.out.println("Så konventionellt");
break;
}
Avancerade saker
Avancerade saker
En enum-typ är väldigt lik en klass. När man kompilerar en enum så skapas en class-fil för den och när man sedan kör sitt program så skapas ett objekt för varje element i uppräkningen. Detta skiljer sig ganska mycket från språk som till exempel C# där en enum-värdena snarare är glorifierade heltalskonstanter, dvs. de byts ut till heltalskonstanter av kompilatorn. Eftersom enum-typer i Java är så närbesläktade med klasser är det också möjligt att lägga till fält och metoder i dem. Det är svårt att hitta något bra exempel på det. Så här ser ett mindre bra exempel ut:
public enum Kundkategori {
BARN, VUXEN, PENSIONÄR;
public int pris;
public void rea() {
pris = pris / 2;
}
}
När programmet kör skapas 3 unika objekt av typen Kundkategori. Variablerna BARN, VUXEN och PENSIONÄR är referenser till dessa objekt. Varje objekt får varsin prisvariabel, på samma sätt som om Kundkategori var en klass. Metoden rea kan anropas på vart och ett av objekten också det på samma sätt som om det vore frågan om en klass:
Kundkategori.BARN.pris = 5;
Kundkategori.VUXEN.pris = 10;
Kundkategori x;
x = Kundkategori.VUXEN;
x.rea();
För att initiera medlemsvariabler går det att ha med en konstruktor som kräver argument. Dessa argument måste man då lägga till för varje element:
public enum Kundkategori {
BARN(5), VUXEN(10), PENSIONÄR(5);
public Kundkategori(int startpris) {
pris = startpris;
}
public int pris;
public void rea() {
pris = pris / 2;
}
}
Det går till och med att låta enum-typer deklarera interface, men det går inte att använda dem i samband med arv. När man ser hur lik en klass enum-typen är så är det lätt att vilja skapa egna objekt utifrån den, men det går inte. Även om man deklarerar konstruktorn att vara public så kan det aldrig skapas några andra objekt än de som är uppräknade som element i enum-typen. Det hela liknar designmönstret singleton, som ju bara tillåter att ett enda objekt skapas av en klass, men här är det snarare frågan om en någrastyckenton eller på engelska a-few-ton. En instans för varje element. Tänk noga efter om det finns någon anledning till att begränsa antalet instanser genom att använda en enum istället för en klass. Kanske vill man ha möjlighet att skapa nya kundkategorier efter behov. (Det är dock oetiskt att vara med och utveckla datorprogram som hjälper handlare att administrera oskicket med speciella medlemskunder, ett system som ju bara är till för att människor som inte vill bära runt på ett tjog fula plastbrickor ska känna sig lurade på 3 kronor varje gång de handlar laxfilé.) De instanser som skapats i enum-typen är tillgängliga överallt i programmet. Vilket kan vara tveksamt om det är bra, av samma anledningar som gör att det ofta inte är världens bästa idé att använda globala variabler.
Det är möjligt att lägga till metoder som är specifika för ett visst element i en enum. Gör inte det. Om du ser det i programkod så titta åt ett annat håll. Här är ett exempel där jag överlagrar toString-metoden för elementet VUXEN:
public enum Kundkategori {
BARN,
VUXEN {
public String toString() {
return "*** Vuxen ***";
}
},
PENSIONÄR;
}
Det som händer är att kompilatorn automatiskt skapar en speciell subklass för just den konstanten. När man känner att en enum behöver många fält, metoder och specifika metoder för vissa element bör man starkt överväga att skriva en klass istället.
Slutsatser
Slutsatser
Jag tycker att enum är en bra sak. Nu kan man på ett bra sätt beskriva att variabeln "kön" måste ha något av värdena "man" och "kvinna" och man slipper långdragna diskussioner om det är man eller kvinna som ska representeras av true (eller 1). Om det man ska räkna upp behöver ha tillstånd och beteende, dvs. fält och metoder så är det nog oftast en klass man bör skriva.
Titta gärna in till min blogg: www.jensgustavsson.se
0 Kommentarer