Enums
We kennen in Java de primitieve types zoals int's, long's, ... Variabelen van deze types kunnen waarden bevatten die uit een bepaalde verzameling komen. Zo zal een int een geheel getal zijn dat kan liggen tussen -2,147,483,648 en +2,147,483,647 (deze grenzen inclusief). Maar soms willen we een eigen verzameling mogelijkheden defini�ren. Dit is nu mogelijk met de nieuwe Java 1.5, meer specifiek met de enumeratie-techniek, kortweg enums genoemd. (Dit is niet een van de eenvoudigste onderwerpen, dus bij meer vragen, stel ze gerust;))
Hoe gebeurde het vroeger?
We konden vroeger reeds enkele waarden gaan defini�ren. Dit gebeurde typisch met een reeks van 'public static final int's, die dan elk een eigen naam kregen. De code zag er dan als volgt uit:
Code:
public static final int NOORD = 0;
public static final int OOST = 1;
public static final int ZUID = 2;
public static final int WEST = 3;
En waar we een variabele willen gebruiken die 1 van deze waarden bevat kunnen we schrijven:
Code:
int windrichting = NOORD;
Deze manier van programmeren heeft echter een groot aantal nadelen:
- Deze techniek is niet typesafe: als we een methode schrijven die 1 van deze 4 waarden verwacht, kan geen garantie afgedwongen worden over de geldigheid van deze waarde. Een methode zal als argument namelijk een int-waarde moeten aanvaarden, maar er kan dus evengoed een andere int-waarde worden doorgegeven dan ��n van deze 4. Binnen de methode zal dus moeten gecontroleerd worden op de geldigheid van de parameter (en eventueel een fout moeten worden opgeworpen).
- We hebben de int's wel een naam gegeven, maar het blijven int's...: als we een dergelijke enum-waarde gaan printen krijgen we een int en verliezen we op die manier informatie over de waarde.
- Stel we breiden de waarden uit of we veranderen hun volgorde...: nu zullen de constanten andere ints voorstellen met als gevolg dat we de code die er gebruik van maakte ook moeten hercompileren (constante ints worden in de gebruikerscode gecompileerd).
Deze nadelen vallen weg door gebruik te maken van de nieuwe enums.
Opsommingen - enums
Laten we direct met de deur in huis vallen en even de aangepast code bekijken:
Code:
enum Windrichting {NOORD, OOST, ZUID, WEST}
En waar we nu 1 van deze waarden willen gebruiken in een variabele windrichting:
Code:
Windrichting richting = Windrichting.NOORD;
Een iets groter voorbeeld zal deze techniek duidelijker maken:
Code:
public class Weerbericht{
public enum Windrichting {NOORD, OOST, ZUID, WEST};
private Windrichting richting;
public Weerbericht(Windrichting richting){
this.richting = richting;
}
public Windrichting getWindrichting(){
return richting;
}
public static void main(String args[]){
Weerbericht test = new Weerbericht(Windrichting.NOORD);
System.out.println(test.getWindrichting());
}
}
Als we dit uitvoeren zien we op het scherm "NOORD" verschijnen! Dit is al 1 van de voordelen van de nieuwe techniek, want bij de oude zou er een 0 worden geprint...
Maar dat is niet het enige: omdat we nu een type hebben gemaakt kan aan de constructor enkel 1 van de geldige Windrichting-waarden worden meegegeven. We moeten dus zelf geen controles meer gaan doorvoeren.
De enums hebben daarnaast ook nog enkele andere eigenschappen, de belangrijkste zijn:
- Het typesafe zijn van de enums kan worden afgedwongen doordat de enums in feite ook klassen zijn, meerbepaald erven ze over van java.lang.Enum. Omdat we dus eigenlijk een klasse defini�ren is de ; na "public enum Windrichting {NOORD, OOST, ZUID, WEST}" in bovenstaand voorbeeld niet verplicht, probeer het maar eens zonder ;)
- Het zijn klassen, en ze defini�ren dus ook een toString()-methode, die standaard de stringwaarde afprint die de programmeur eraan heeft gegeven (vandaar dat in ons voorbeeld NOORD wordt afgeprint). Men kan ook besluiten om deze methode te overschrijven (zie verder).
- Enum-waarden kunnen niet uitgebreid worden via overerving (ze zijn 'final')
- Een enum-declaratie is statisch binnen de klasse waarin ze gedefinieerd is (het heeft dus geen zin ze expliciet statisch te declareren zoals in: public static enum ...)
- Enum-waarden kunnen vergeleken worden via == en/of met equals()-methode
- Met values() kan een lijst van mogelijke waarden worden opgevraagd bij de enumeratie
Switching
Tot voor 1.5 werkte de switch() enkel met int, short, char en byte-waarden, maar nu dus ook met enum-waarden :) Dit werkt heel eenvoudig (we gebruiken de enum Windrichting uit bovenstaand voorbeeld):
Code:
Windrichting richting = ...;
...
switch(richting) {
case NOORD: System.out.println("Noordenwind");
break;
case OOST: System.out.println("Oostenwind");
break;
case ZUID: System.out.println("Zuidenwind");
break;
case WEST: System.out.println("Westenwind");
break;
}
Hierbij valt op dat we gewoon NOORD kunnen gebruiken ipv Windrichting.NOORD. Dit komt doordat de compiler het type van argument in de switch() kent, en zo zelf intern NOORD veranderd naar Windrichting.NOORD...
Functionaliteit toevoegen
Zoals reeds vermeld zijn enums niets anders dan Java-klassen. Een object dat we van een klasse maken kent een toestand (z'n veranderlijken) en een gedrag (z'n methoden). Dit wil dus zeggen dat het mogelijk moet zijn om methoden toe te gaan voegen aan enums, en dat kan dus ook! :)
We voegen bijvoorbeeld een methode draai90graden toe aan de enum. Deze stelt het draaien van de wind voor in de richting zoals we de zon zien draaien (met de klok mee dus ;)):
Code:
public enum Windrichting{
NOORD,
OOST,
ZUID,
WEST;
public Windrichting draai90graden(){
Windrichting ret = NOORD; //initialisatie om compiler te sussen ;)
switch(this){
case NOORD: ret = OOST; break;
case OOST: ret = ZUID; break;
case ZUID: ret = WEST; break;
case WEST: ret = NOORD;
}
return ret;
}
}
Let hierbij op de ; na de laatste enum-waarde! Het is ook belangrijk op te merken dat de opsomming steeds als eerste moet gegeven worden, alvorens de methoden worden beschreven. Er kunnen na de enum-waarden zelfs eerst nog enkele variabelen-declaraties volgen.
Verder is ook hier intern gebruik gemaakt van NOORD zonder Windrichting.NOORD te gebruiken: omdat dit intern gedefinieerd is kent de compiler ook de herkomst van deze enum-waarden en levert dit geen problemen op.
We kunnen voorgaand volledig voorbeeld uitbreiden en dan bekomen we:
Code:
public class Weerbericht{
public enum Windrichting{
NOORD,
OOST,
ZUID,
WEST;
public Windrichting draai90graden(){
Windrichting ret = OOST;
switch(this){
case NOORD: ret = OOST; break;
case OOST: ret = ZUID; break;
case ZUID: ret = WEST; break;
case WEST: ret = NOORD;
}
return ret;
}
}
private Windrichting richting;
public Weerbericht(Windrichting richting){
this.richting = richting;
}
public Windrichting getWindrichting(){
return richting;
}
public static void main(String args[]){
Weerbericht test = new Weerbericht(Windrichting.NOORD);
System.out.println(test.getWindrichting());
System.out.println(test.getWindrichting().draai90graden());
}
}
Dit programma heeft als uitvoering:
NOORD
OOST
Een klasse kent ook een constructor, maar hoe werkt dit dan bij enums zul je vragen. Want die hebben een bepaalde waarde, afhankelijk van de waarde die de programmeur opgaf in z'n code. Verder is de constructor van een enum private, we kunnen dus zelf geen objecten gaan aanmaken via deze constructor. Toch is het mogelijk om deze functionaliteit uit te buiten. Volgend voorbeeld maakt hiervan gebruik:
Code:
public enum Windrichting{
NOORD(0),
OOST(90),
ZUID(180),
WEST(270);
private int graden;
//de waarde tussen ( en ) bij de waarde zal gebruikt worden als
//argument voor de constructor en wordt bijgehouden in graden
Windrichting(int graden){ this.graden = graden;}
public Windrichting draai90graden(){
Windrichting ret = OOST;
switch(this){
case NOORD: ret = OOST; break;
case OOST: ret = ZUID; break;
case ZUID: ret = WEST; break;
case WEST: ret = NOORD;
}
return ret;
}
public int geefRichtingInGraden(){
return graden;
}
}
De waarde die volgt op de enum-waarde zal gebruikt worden als argument voor de constructor. (Dit kunnen ook meerdere zijn gescheiden door een komma)
Nogmaals (tot vervelens toe wellicht;)): enums zijn klassen, we kunnen ze dus ook bv interfaces laten implementeren:
Code:
public enum Enumnaam implements Interfacenaam {...}
waarbij dan de benodigde methodes uit de interface moeten ge�mplementeerd worden.
Wat dan weer niet gaat is enums uitbreiden, dit door het final karakter van de enums (van een final gedeclareerde klasse is ook niet over te erven).
Enums en collecties
Dankzij generics zijn er nu ook enkele nuttige toepassingen op de combinatie van collecties en generics. Meer daarover in 1 van de volgende afleveringen die zal handelen over generics in Java Tiger.
Na dit iets zwaardere stuk gaan we de volgende keer een kijkje nemen naar de uitgebreide for-lus in Java Tiger! Tot dan;)
Lees ook de voorgaande delen van deze reeks:
Deel 1 : VarArgs
Deel 2 : Boxing
(Vragen, aanvullingen, verbeteringen en opmerkingen zijn steeds welkom! :))
ps: Als iemand weet hoe ik indentatie kan goedkrijgen in code-tags, laat het maar weten, de code wordt zo wat onduidelijk... :)