# Programmeren in Java/Enums

Enums, of ook wel enumeraties of opsommingen genoemd, zijn een speciaal soort datatype in Java. Ze worden gebruikt om een groep van constanten samen te houden die logischerwijs bij elkaar horen. Neem nu de maanden van het jaar. Je weet dat januari de eerste maand is van het jaar en februari de twee enzoverder... Je wilt ergens in je code constanten bijhouden die dit bijhoudt om bijvoorbeeld met datums te kunnen werken. Dus je zou in je code het volgende kunnen doen:
Dit zal goed werken, maar het kan beter. Wat als we dit willen gebruiken op andere plekken in onze code, dan zouden we wellicht deze code moeten kopiëren en zodra je code moet beginnen kopiëren om het te kunnen hergebruiken is het beter om een alternatief te vinden. Daarvoor worden dus enums gebruikt.

## Basis

Zoals eerder werd vermeld is een enum een datatype dat een groep van constanten samenhoudt. Je kunt het zien als een soort klasse, maar een klasse waarvan je zelf geen instanties kunt aanmaken met een constructor en het sleutelwoord new, maar enkel pregedefiniëerde instanties kunt gebruiken. Een simpele enum aanmaken doe je als volgt.
Het is gelijkaardig met het maken van een gewone klasse maar in de plaats van het sleutelwoord class moet je enum gebruiken. Binnen de enum kun je dan de mogelijk instanties van de enum oplijsten, gescheiden door een komma en afgesloten met een puntkomma (;). De Java conventie zegt dat de namen van de verschillende enum instanties in hoofdletters moeten zijn(net zoals constanten) maar het zal geen fouten geven als je dit toch niet zou doen. Je kunt de enum in een eigen .java-bestand bewaren of ergens tussen voegen bij een andere klasse zoals bijvoorbeeld op de volgende manier.
Een instantie van een enum oproepen doe je door de naam van de enum te gebruiken, gevolgd door een punt, met daarna de naam van de instantie die je nodig hebt, bijvoorbeeld Month.APRIL. Je kunt het dan als volgt gebruiken:
Nu hebben we nog niet voor iedere maand de "maand van het jaar"-eigenschap.

## Enums in variabelen, constanten en functies

Enums kun je ook makkelijk in een variabelen of constanten stoppen. Je doet dit simpelweg door een variabele of constante te declareren en dan vervolgens te initialiseren met een instantie van een enum. Bijvoorbeeld als volgt.
Je vermeld dus als datatype de naam van de enum. En net als met alle andere datatypes kun je het gebruiken als datatype voor een parameter in een functie en als datatype voor een returnwaarde.

## Eigenschappen en de constructor

Een eigenschap toevoegen is even simpel als een eigenschap toevoegen bij gewone klassen.
We kunnen dus net zoals bij een klasse een eigenschap toevoegen door er één te declareren al dan niet met een access modifier. Als je geen access modifier gebruikt of public, kun je de eigenschap oproepen als volgt Month.APRIL.monthOfYear. Je zult echter wel meteen merken dat we de de variabele niet geïnitialiseerd hebben. Dus als we de enum gebruiken als volgt krijgen we niet het gewenste resultaat.
We krijgen "0" als output, omdat de standaardwaarde van een niet-geïnitialiseerde int nul is en dat is niet wat we willen hebben. We kunnen het ook niet rechtstreeks initaliseren in de enum zelf door bijvoorbeeld als volgt te doen:
Dit zal ervoor zorgen dat alle instanties van de enum nul hebben als waarde hebben voor deze eigenschap. De oplossing is simpel, net zoals bij gewone klassen kunnen we een constructor gebruiken om waardes te geven aan een instantie van een enum. Er werd eerder in dit hoofdstuk gezegd dat we geen instanties konden maken met een constructor. Dit is niet helemaal correct, het is namelijk wel mogelijk om dit te doen, maar enkel binnen de enum zelf. Een constructor toevoegen, doe je net zoals bij een gewone klasse. Je maakt een nieuwe methode aan met de naam van de enum.
Je moet geen access modifier vermelden bij de constructor van een enum, dit is omdat de enige die je mag gebruiken private is (private omdat je de constructor enkel binnen de enum mag gebruiken) en als je het zou vermelden het daardoor eigenlijk overbodig is. Nu hebben we echter nog steeds niet de eigenschap monthOfYear geïnitialiseerd, dit kun je net als bij andere constructors als volgt doen:
De constructor heeft als parameter monthOfYear die we via het sleutelwoord this kunnen kopiëren naar de eigenschap monthOfYear van de enum. Maar we zijn er nog steeds niet, we hebben nog steeds nergens een waarde gegeven om te zeggen welke maand van het jaar een bepaalde maand is. Als je een IDE gebruikt zul je nu een error krijgen bij de opsomming van de verschillende maanden of krijg je een error bij het compileren van de java code. Wellicht krijg je dan iets in de aard van:
We moeten dus bij iedere maand een argument meegeven waar we zeggen welke maand van het jaar iedere maand is.
Telkens wanneer de enum wordt aangemaakt, wordt de constructor opgeroepen om een instantie te maken van elke maand met de juiste waarde voor de eigenschap monthOfYear. Je kunt net zoals bij gewone klassen ook meerdere constructors definiëren. We kunnen dan ook nu de eigenschap correct gebruiken.

## Ingebouwde methodes

Net zoals gewone klassen hebben enums ingebouwde methodes. In dit stuk gaan we er een aantal overlopen. Je moet wel een onderscheid maken tussen de methodes van de enum zelf en één van de instanties van de enum, die zijn namelijk verschillend.
Een instantie van de enum heeft onder andere:
* name: Geeft de naam van de instantie, bijvoorbeeld voor Month.MAY.name wordt dat "MAY".
* equals: Hiermee kun je twee instanties van een enum met elkaar vergelijken, net zoals gewone objecten.
* ordinal: Kun je vergelijken met de index van array. Het is af te raden om deze methode te gebruiken.
* toString: Geeft net zoals name de naam van de instantie.
Het is niet mogelijk om deze methodes te overschrijven zoals bij klassen, behalve toString maar dat wordt ook sterk afgeraden.
De enumklasse zelf heeft onder andere:
* values: Deze geeft een array met al de mogelijke instanties van de enum in kwestie, handig voor te itereren met een lus.
* valueOf: Geeft een instantie terug van een enum op basis van een meegeleverde String. De String wordt dan vergeleken met naam van de mogelijke instanties van de enum en de passende instantie wordt terug gegeven. Bijvoorbeeld: Month.valueOf("MAY") geeft de instantie MAY terug van de enum MONTH.

## Eigen methodes

Je kunt ook eigen methodes schrijven voor een enum. Hier moet een onderscheid gemaakt worden tussen methodes die gelinkt zijn aan de enumklasse zelf zoals Month.values of die gelinkt zijn aan een instantie van een enum zoals Month.APRIL.name. De onderscheid tussen de twee in de code wordt gemaakt door het sleutelwoord static.

### Statische methodes

Als voorbeeld van een statische methode gaan we een methode aanmaken aanmaken die de maanden van het jaar terug geeft die 31 dagen lang zijn.
Je kunt nu deze methode gebruiken als volgt Month.getMonthsWith31Days en krijg je dus een array terug met de maanden van het jaar die 31 dagen lang zijn.

### Instantie methodes

Je kunt dus ook methodes hebben voor de instanties van de enum, dit doe je door het sleutelwoord weg te laten. In onderstaand voorbeeld, hebben we een extra eigenschap toegevoegd aan de enum voor het aantal dagen in de (juliaanse) maand en getters geplaatst om de waarde van de eigenschappen monthOfYear en daysInMonth op te roepen.

## Gebruiken met de switch

Je kunt enums ook gebruiken met een switch, laten we daarvoor een nieuwe enum maken met als naam Compass die de vier windrichtingen bevat.
We willen de omgekeerde windrichting krijgen als uitvoer, bijvoorbeeld NORTH moet als output geven SOUTH, EAST geeft als output WEST etc. We kunnen daarvoor de switch gebruiken zoals hieronder.
In de switch vermeld je dan bijvoorbeeld een variable die een de instantie van een enum bevat of kun je zoals hier de enum vermelden met een instantie (Compass.NORTH). Omdat je een enum gebruikt moet je bij case enkel de instantienamen van de enum vermelden. Echter zou het misschien beter te zijn om de switch te gebruiken in een methode van de enum aangezien dit logischerwijs bij elkaar hoort. Zoals volgt:
We kunnen in de switch gebruik maken van het sleutelwoord this om dan met de switch de tegenovergestelde windrichting te krijgen. Maar hiermee wordt ook misschien duidelijk waarom het over het algemeen af te raden is om de switch te gebruiken, het is moeilijk uitbreidbaar. Stel dat we de enum aanpassen om nog meer windrichtingen te bevatten, bijvoorbeeld NORTHEAST voor het noordoosten. Wat gaat de switch dan doen?
Als we blindelings windrichtingen toevoegen aan de enum, dan kan dit rampzalige gevolgen hebben als we de switch niet aanpassen. De switch zal hier een null geven voor alle windrichtingen die het niet verwerkt, wat voor fouten kan zorgen in de rest van je programma code die geen nulls verwachten. Dan zijn er ook nog de mogelijk andere switchen die de enum gebruiken.

## Overerving

Een enum kan niet overerven van een klasse of een andere enum, dit komt omdat een enum al overerft van Enum<E>. Enum<E> is dan ook de klasse waarvan een enum zijn ingebouwde functies heeft gekregen. Maar een enum kan wel binnen zichzelf overerven en daarmee ook overriden. Neem de code van het vorige stuk waarbij we tegenovergestelde windrichting wouden weten.
We maakten van de methode getOppositeDirection een abstracte methode. Dit geeft natuurlijk wel meteen het probleem dat een abstracte methode ergens geïmplementeerd moet worden. Dit kun je doen ter hoogte van iedere instantie van de enum zoals volgt.
Als je dan nu Compass.NORTH.getOppositeDirection gebruikt zul je SOUTH terug krijgen. Je bent niet verplicht om de methode abstract te maken om ze daarna te kunnen overriden. Je kunt ieder methode die niet final is overriden. Maar met abstract verplicht je wel een ontwikkelaar om een implementatie te doen van die methode telkens er instantie bij de enum wordt toegevoegd.
Het is ook mogelijk om interfaces te laten implementeren.